diff --git a/config/vufind/config.ini b/config/vufind/config.ini index b0b9bf04c4a8ebb3a2a48a98bb3f3927df960ea4..b91bbc626d33b2897695e7d1c52a916044f781fb 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -739,11 +739,29 @@ authors = Wikipedia ; recommendation module in searches.ini for more information) ;europeanaAPI = INSERTKEY -; If this is set, a new map tab will show on the record page for records which -; have long_lat data (see import/marc_local.properties for more information). -; The setting specifies the type of map; currently, the only supported value is -; "google" -;recordMap = google +; Geographic Display +; recordMap can be set to either 'google' or 'openlayers'; +; 'google' will only display point features +; 'openlayers' will display point and rectangle features. Default setting +; (see import/marc_local.properties for more information). +; Map Tab Options: +; mapLabels: leave empty, file:filename, or driver +; Leave it empty – no map labels will be displayed (default) +; file:filename - specify a file name after the colon for the +; coordinate/label lookup file. Coordinates in file must +; be specified as WENS. +; driver - Use the getCoordinateLabels method of the record driver to fetch labels; +; by default this relies on the long_lat_label field in Solr, +; but you can override the behavior with custom record driver code. +; The field must be the same length as the number of coordinate sets. +; Coordinates will be matched to labels on an ordered basis such that +; label[0] will be assigned for coordinate[0] and so forth. +; displayCoords: true or false. Default is false. (Only for recordMap = openlayers) +; If displayCoords is true, then the coordinate values from +; coordinate field will be displayed before the map label in the label popup. +recordMap = openlayers +mapLabels = file:geosearch_test_lookup.txt +displayCoords = true ; If you set recordMap = google, then Google requires that you obtain an API key. ; For more information on obtaining an API key, see: diff --git a/config/vufind/searches.ini b/config/vufind/searches.ini index 51ef358741c335ace64fff40b46ea6ec09f19a32..baf7ea2ef6930ae1a578d86f5111087ab8197215 100644 --- a/config/vufind/searches.ini +++ b/config/vufind/searches.ini @@ -51,6 +51,7 @@ case_sensitive_ranges = true ; [NoResultsRecommendations] sections below. ; See the comments above those sections for details on legal settings. You may ; repeat these lines to load multiple recommendations. +;default_top_recommend[] = MapSelection ; see [MapSelection] below for details default_top_recommend[] = TopFacets:ResultsTop default_top_recommend[] = SpellingSuggestions ;default_top_recommend[] = VisualFacets:Visual_Settings @@ -120,6 +121,7 @@ Author = Author Subject = Subject CallNumber = "Call Number" ISN = "ISBN/ISSN" +;Coordinate = Coordinates tag = Tag ; This section defines which search options will be included on the advanced @@ -136,6 +138,8 @@ publisher = adv_search_publisher Series = adv_search_series year = adv_search_year toc = adv_search_toc +;Coordinate = Coordinates + ; This section defines the sort options available on standard search results. ; Values on the left of the equal sign are either the reserved term "relevance" @@ -317,6 +321,13 @@ CallNumber = callnumber-sort ; AuthorityRecommend:__resultlimit__:50 then authority recommendations will ; only display on result screens displaying fewer than 50 hits; by default, ; recommendations will always display). Filtering is optional. +; MapSelection:[ini section]:[ini name] +; Enable geographic searching capability via OpenLayers3 API by activating +; this module. Records must be indexed using the geographic search and display +; fields. See the marc_local.properties file for more information on indexing. +; Default settings and more comments may be found in the [MapSelection] +; section in this file. The section name and ini file name loaded by the +; module may be overridden through the [ini section]/[ini name] parameters. ; PubDateVisAjax:[zooming]:[facet field 1]:[facet field 2]:...:[facet field n] ; Display a visualization of publication dates for each of the specified facet ; fields. This is designed for a field containing four-digit years. Zooming @@ -493,6 +504,7 @@ Author = "Solr:Author:author,author2" Subject = "Solr:Subject:topic,genre,geographic,era" CallNumber = "SolrCN" ISN = "Solr:ISN:isbn,issn" +Coordinate = "None" tag = "Tag" ; When snippets are enabled, this section can be used to display captions based on @@ -607,6 +619,26 @@ view=full ; from the actual record by period, e.g. testsrc.12345) ;sources = alli,testsrc +; This section defines the default parameters for the geographic search +; functionality found in the MapSelection recommendation module. +; To enable this feature, uncomment the default_top_recommend[] = MapSelection +; in the default recommendations section. To set the configuration settings +; for this feature, adjust the parameters below. +[MapSelection] +; This defines the coordinates of a search region that will be highlighted when +; the user clicks the "Geographic Search" link next to the VuFind search box. +; This should ideally cover a large area of the map where most/all of your +; geographic points are located. If your dataset is not concentrated in one +; geographic area, it is advised that you pick a default area, and do not use +; the entire extent of the map for searching (otherwise the search may be slow). +; The default coordinates specified below are in decimal degrees, and are +; ordered as WENS (west, east, north, south). Ranges of valid values are: +; -180 to 180 (longitude) and -85 to 85 (latitude). Note, to search from and to +; the international date line, use west = -179 and east = -180. +default_coordinates = "-95, 30, 72, 15" +; height: Height in pixels of the map selection interface. +height = 320 + ; This section defines settings used to fetch similar records. [MoreLikeThis] ; Boolean value indicating whether the newer MoreLikeThis query handler should be diff --git a/config/vufind/searchspecs.yaml b/config/vufind/searchspecs.yaml index 24a8575e46ff9023ee233707dace405e968a0fd5..5e94d5cf055dab578a701f352ea878066470e5b9 100644 --- a/config/vufind/searchspecs.yaml +++ b/config/vufind/searchspecs.yaml @@ -194,6 +194,12 @@ Subject: # DismaxFields: # - topic_unstemmed^150 + +Coordinate: + DismaxFields: + - long_lat_display + DismaxHandler: edismax + # This field definition is a compromise that supports both journal-level and # article-level data. The disadvantage is that hits in article titles will # be mixed in. If you are building a purely article-oriented index, you should @@ -264,6 +270,7 @@ AllFields: - description - isbn - issn + - long_lat_display DismaxHandler: edismax # ExactSettings: # DismaxFields: diff --git a/import/index_java/src/org/solrmarc/index/VuFindIndexer.java b/import/index_java/src/org/solrmarc/index/VuFindIndexer.java index 01bcfbdf09454f0a612d5b71a125f869dfa0df78..3c31858b0aa6376064f816208c5ef32d6e50eb22 100644 --- a/import/index_java/src/org/solrmarc/index/VuFindIndexer.java +++ b/import/index_java/src/org/solrmarc/index/VuFindIndexer.java @@ -35,6 +35,7 @@ import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Properties; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -81,6 +82,10 @@ public class VuFindIndexer extends SolrIndexer private SimpleDateFormat marc005date = new SimpleDateFormat("yyyyMMddHHmmss.S"); private SimpleDateFormat marc008date = new SimpleDateFormat("yyMMdd"); + private static final Pattern COORDINATES_PATTERN = Pattern.compile("^([eEwWnNsS])(\\d{3})(\\d{2})(\\d{2})"); + private static final Pattern HDMSHDD_PATTERN = Pattern.compile("^([eEwWnNsS])(\\d+(\\.\\d+)?)"); + private static final Pattern PMDD_PATTERN = Pattern.compile("^([+-])(\\d+(\\.\\d+)?)"); + private static ConcurrentHashMap<String, Ini> configCache = new ConcurrentHashMap<String, Ini>(); // Shutdown flag: @@ -1380,6 +1385,225 @@ public class VuFindIndexer extends SolrIndexer } /** + * The following several methods are designed to get latitude and longitude + * coordinates. + * Records can have multiple coordinates sets of points and/or rectangles. + * Points are represented by coordinate sets where N=S E=W. + * + * code adapted from xrosecky - Moravian Library + * https://github.com/moravianlibrary/VuFind-2.x/blob/master/import/index_scripts/geo.bsh + * and incorporates VuFind location.bsh functionality for GoogleMap display. + */ + + /** + * Convert MARC coordinates into bbox_geo format. + * + * @param Record record + * @return List geo_coordinates + */ + public List<String> getAllCoordinates(Record record) { + List<String> geo_coordinates = new ArrayList<String>(); + List<VariableField> list034 = record.getVariableFields("034"); + if (list034 != null) { + for (VariableField vf : list034) { + DataField df = (DataField) vf; + String d = df.getSubfield('d').getData(); + String e = df.getSubfield('e').getData(); + String f = df.getSubfield('f').getData(); + String g = df.getSubfield('g').getData(); + //System.out.println("raw Coords: "+d+" "+e+" "+f+" "+g); + + // Check to see if there are only 2 coordinates + // If so, copy them into the corresponding coordinate fields + if ((d !=null && (e == null || e.trim().equals(""))) && (f != null && (g==null || g.trim().equals("")))) { + e = d; + g = f; + } + if ((e !=null && (d == null || d.trim().equals(""))) && (g != null && (f==null || f.trim().equals("")))) { + d = e; + f = g; + } + + // Check and convert coordinates to +/- decimal degrees + Double west = convertCoordinate(d); + Double east = convertCoordinate(e); + Double north = convertCoordinate(f); + Double south = convertCoordinate(g); + + // New Format for indexing coordinates in Solr 5.0 - minX, maxX, maxY, minY + // Note - storage in Solr follows the WENS order, but display is WSEN order + String result = String.format("ENVELOPE(%s,%s,%s,%s)", new Object[] { west, east, north, south }); + + if (validateCoordinates(west, east, north, south)) { + geo_coordinates.add(result); + } + } + } + return geo_coordinates; + } + + /** + * Get point coordinates for GoogleMap display. + * + * @param Record record + * @return List coordinates + */ + public List<String> getPointCoordinates(Record record) { + List<String> coordinates = new ArrayList<String>(); + List<VariableField> list034 = record.getVariableFields("034"); + if (list034 != null) { + for (VariableField vf : list034) { + DataField df = (DataField) vf; + String d = df.getSubfield('d').getData(); + String e = df.getSubfield('e').getData(); + String f = df.getSubfield('f').getData(); + String g = df.getSubfield('g').getData(); + + // Check to see if there are only 2 coordinates + if ((d !=null && (e == null || e.trim().equals(""))) && (f != null && (g==null || g.trim().equals("")))) { + Double long_val = convertCoordinate(d); + Double lat_val = convertCoordinate(f); + String longlatCoordinate = Double.toString(long_val) + ',' + Double.toString(lat_val); + coordinates.add(longlatCoordinate); + } + if ((e !=null && (d == null || d.trim().equals(""))) && (g != null && (f==null || f.trim().equals("")))) { + Double long_val = convertCoordinate(e); + Double lat_val = convertCoordinate(g); + String longlatCoordinate = Double.toString(long_val) + ',' + Double.toString(lat_val); + coordinates.add(longlatCoordinate); + } + // Check if N=S and E=W + if (d.equals(e) && f.equals(g)) { + Double long_val = convertCoordinate(d); + Double lat_val = convertCoordinate(f); + String longlatCoordinate = Double.toString(long_val) + ',' + Double.toString(lat_val); + coordinates.add(longlatCoordinate); + } + } + } + return coordinates; + } + + /** + * Get all available coordinates from the record. + * + * @param Record record + * @return List geo_coordinates + */ + public List<String> getDisplayCoordinates(Record record) { + List<String> geo_coordinates = new ArrayList<String>(); + List<VariableField> list034 = record.getVariableFields("034"); + if (list034 != null) { + for (VariableField vf : list034) { + DataField df = (DataField) vf; + String west = df.getSubfield('d').getData(); + String east = df.getSubfield('e').getData(); + String north = df.getSubfield('f').getData(); + String south = df.getSubfield('g').getData(); + String result = String.format("%s %s %s %s", new Object[] { west, east, north, south }); + if (west != null || east != null || north != null || south != null) { + geo_coordinates.add(result); + } + } + } + return geo_coordinates; + } + + /** + * Check coordinate type HDMS HDD or +/-DD. + * + * @param String coordinateStr + * @return Double coordinate + */ + protected Double convertCoordinate(String coordinateStr) { + Double coordinate = Double.NaN; + Matcher HDmatcher = HDMSHDD_PATTERN.matcher(coordinateStr); + Matcher PMDmatcher = PMDD_PATTERN.matcher(coordinateStr); + if (HDmatcher.matches()) { + String hemisphere = HDmatcher.group(1).toUpperCase(); + Double degrees = Double.parseDouble(HDmatcher.group(2)); + // Check for HDD or HDMS + if (hemisphere.equals("N") || hemisphere.equals("S")) { + if (degrees > 90) { + String hdmsCoordinate = hemisphere+"0"+HDmatcher.group(2); + coordinate = coordinateToDecimal(hdmsCoordinate); + } else { + coordinate = Double.parseDouble(HDmatcher.group(2)); + if (hemisphere.equals("S")) { + coordinate *= -1; + } + } + } + if (hemisphere.equals("E") || hemisphere.equals("W")) { + if (degrees > 180) { + String hdmsCoordinate = HDmatcher.group(0); + coordinate = coordinateToDecimal(hdmsCoordinate); + } else { + coordinate = Double.parseDouble(HDmatcher.group(2)); + if (hemisphere.equals("W")) { + coordinate *= -1; + } + } + } + return coordinate; + } else if (PMDmatcher.matches()) { + String hemisphere = PMDmatcher.group(1); + coordinate = Double.parseDouble(PMDmatcher.group(2)); + if (hemisphere.equals("-")) { + coordinate *= -1; + } + return coordinate; + } else { + return null; + } + } + + /** + * Convert HDMS coordinates to decimal degrees. + * + * @param String coordinateStr + * @return Double coordinate + */ + protected Double coordinateToDecimal(String coordinateStr) { + Matcher matcher = COORDINATES_PATTERN.matcher(coordinateStr); + if (matcher.matches()) { + String hemisphere = matcher.group(1).toUpperCase(); + int degrees = Integer.parseInt(matcher.group(2)); + int minutes = Integer.parseInt(matcher.group(3)); + int seconds = Integer.parseInt(matcher.group(4)); + double coordinate = degrees + (minutes / 60.0) + (seconds / 3600.0); + if (hemisphere.equals("W") || hemisphere.equals("S")) { + coordinate *= -1; + } + return coordinate; + } + return null; + } + + /** + * Check decimal degree coordinates to make sure they are valid. + * + * @param Double west, east, north, south + * @return boolean + */ + protected boolean validateCoordinates(Double west, Double east, Double north, Double south) { + if (west == null || east == null || north == null || south == null) { + return false; + } + if (west > 180.0 || west < -180.0 || east > 180.0 || east < -180.0) { + return false; + } + if (north > 90.0 || north < -90.0 || south > 90.0 || south < -90.0) { + return false; + } + if (north < south || west > east) { + return false; + } + return true; + } + + /** + * THIS FUNCTION HAS BEEN DEPRECATED. * Determine the longitude and latitude of the items location. * * @param record current MARC record @@ -1416,7 +1640,7 @@ public class VuFindIndexer extends SolrIndexer val = val + ',' + val2; } } - return val; + return val; } } //otherwise return null diff --git a/import/index_scripts/location.bsh b/import/index_scripts/location.bsh index c382de853038938d15fef7ef760e3e14de071d65..c6e397da95f39da6ff11d94d58bbad236d79f257 100644 --- a/import/index_scripts/location.bsh +++ b/import/index_scripts/location.bsh @@ -1,15 +1,232 @@ /** - * Custom latitude/longitude script. + * Custom script to get latitude and longitude coordinates. + * Records can have multiple coordinates sets + * of points and/or rectangles. + * Points are represented by coordinate sets where N=S E=W. * - * This can be used to override built-in SolrMarc custom functions. If you change - * this script, you will need to activate it in import/marc_local.properties before - * it will be applied during indexing. + * code adapted from xrosecky - Moravian Library + * https://github.com/moravianlibrary/VuFind-2.x/blob/master/import/index_scripts/geo.bsh + * and incorporates VuFind location.bsh functionality for GoogleMap display. + * + */ + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.marc4j.marc.*; + +private static final Pattern COORDINATES_PATTERN = Pattern.compile("^([eEwWnNsS])(\\d{3})(\\d{2})(\\d{2})"); +private static final Pattern HDMSHDD_PATTERN = Pattern.compile("^([eEwWnNsS])(\\d+(\\.\\d+)?)"); +private static final Pattern PMDD_PATTERN = Pattern.compile("^([+-])(\\d+(\\.\\d+)?)"); + +/** + * Convert MARC coordinates into bbox_geo format. + * + * @param Record record + * @return List geo_coordinates + */ +public List getAllCoordinates(Record record) { + List geo_coordinates = new ArrayList(); + List list034 = record.getVariableFields("034"); + if (list034 != null) { + for (VariableField vf : list034) { + DataField df = (DataField) vf; + String d = df.getSubfield('d').getData(); + String e = df.getSubfield('e').getData(); + String f = df.getSubfield('f').getData(); + String g = df.getSubfield('g').getData(); + //System.out.println("raw Coords: "+d+" "+e+" "+f+" "+g); + + // Check to see if there are only 2 coordinates + // If so, copy them into the corresponding coordinate fields + if ((d !=null && (e == null || e.trim().equals(""))) && (f != null && (g==null || g.trim().equals("")))) { + e = d; + g = f; + } + if ((e !=null && (d == null || d.trim().equals(""))) && (g != null && (f==null || f.trim().equals("")))) { + d = e; + f = g; + } + + // Check and convert coordinates to +/- decimal degrees + Double west = convertCoordinate(d); + Double east = convertCoordinate(e); + Double north = convertCoordinate(f); + Double south = convertCoordinate(g); + + // New Format for indexing coordinates in Solr 5.0 - minX, maxX, maxY, minY + // Note - storage in Solr follows the WENS order, but display is WSEN order + String result = String.format("ENVELOPE(%s,%s,%s,%s)", new Object[] { west, east, north, south }); + + if (validateCoordinates(west, east, north, south)) { + geo_coordinates.add(result); + } + } + } + return geo_coordinates; +} + +/** + * Get point coordinates for GoogleMap display. + * + * @param Record record + * @return List coordinates + */ +public List getPointCoordinates(Record record) { + List coordinates = new ArrayList(); + List list034 = record.getVariableFields("034"); + if (list034 != null) { + for (VariableField vf : list034) { + DataField df = (DataField) vf; + String d = df.getSubfield('d').getData(); + String e = df.getSubfield('e').getData(); + String f = df.getSubfield('f').getData(); + String g = df.getSubfield('g').getData(); + + // Check to see if there are only 2 coordinates + if ((d !=null && (e == null || e.trim().equals(""))) && (f != null && (g==null || g.trim().equals("")))) { + Double long_val = convertCoordinate(d); + Double lat_val = convertCoordinate(f); + String longlatCoordinate = Double.toString(long_val) + ',' + Double.toString(lat_val); + coordinates.add(longlatCoordinate); + } + if ((e !=null && (d == null || d.trim().equals(""))) && (g != null && (f==null || f.trim().equals("")))) { + Double long_val = convertCoordinate(e); + Double lat_val = convertCoordinate(g); + String longlatCoordinate = Double.toString(long_val) + ',' + Double.toString(lat_val); + coordinates.add(longlatCoordinate); + } + // Check if N=S and E=W + if (d.equals(e) && f.equals(g)) { + Double long_val = convertCoordinate(d); + Double lat_val = convertCoordinate(f); + String longlatCoordinate = Double.toString(long_val) + ',' + Double.toString(lat_val); + coordinates.add(longlatCoordinate); + } + } + } + return coordinates; +} + +/** + * Get all available coordinates from the record. + * + * @param Record record + * @return List geo_coordinates + */ +public List getDisplayCoordinates(Record record) { + List geo_coordinates = new ArrayList(); + List list034 = record.getVariableFields("034"); + if (list034 != null) { + for (VariableField vf : list034) { + DataField df = (DataField) vf; + String west = df.getSubfield('d').getData(); + String east = df.getSubfield('e').getData(); + String north = df.getSubfield('f').getData(); + String south = df.getSubfield('g').getData(); + String result = String.format("%s %s %s %s", new Object[] { west, east, north, south }); + if (west != null || east != null || north != null || south != null) { + geo_coordinates.add(result); + } + } + } + return geo_coordinates; +} + +/** + * Check coordinate type HDMS HDD or +/-DD. + * + * @param String coordinateStr + * @return Double coordinate + */ +public Double convertCoordinate(String coordinateStr) { + Double coordinate = Double.NaN; + Matcher HDmatcher = HDMSHDD_PATTERN.matcher(coordinateStr); + Matcher PMDmatcher = PMDD_PATTERN.matcher(coordinateStr); + if (HDmatcher.matches()) { + String hemisphere = HDmatcher.group(1).toUpperCase(); + Double degrees = Double.parseDouble(HDmatcher.group(2)); + // Check for HDD or HDMS + if (hemisphere.equals("N") || hemisphere.equals("S")) { + if (degrees > 90) { + String hdmsCoordinate = hemisphere+"0"+HDmatcher.group(2); + coordinate = coordinateToDecimal(hdmsCoordinate); + } else { + coordinate = Double.parseDouble(HDmatcher.group(2)); + if (hemisphere.equals("S")) { + coordinate *= -1; + } + } + } + if (hemisphere.equals("E") || hemisphere.equals("W")) { + if (degrees > 180) { + String hdmsCoordinate = HDmatcher.group(0); + coordinate = coordinateToDecimal(hdmsCoordinate); + } else { + coordinate = Double.parseDouble(HDmatcher.group(2)); + if (hemisphere.equals("W")) { + coordinate *= -1; + } + } + } + return coordinate; + } else if (PMDmatcher.matches()) { + String hemisphere = PMDmatcher.group(1); + coordinate = Double.parseDouble(PMDmatcher.group(2)); + if (hemisphere.equals("-")) { + coordinate *= -1; + } + return coordinate; + } else { + return null; + } +} + +/** + * Convert HDMS coordinates to decimal degrees. + * + * @param String coordinateStr + * @return Double coordinate */ -import org.marc4j.marc.Record; -import org.marc4j.marc.ControlField; -import org.marc4j.marc.DataField; +public Double coordinateToDecimal(String coordinateStr) { + Matcher matcher = COORDINATES_PATTERN.matcher(coordinateStr); + if (matcher.matches()) { + String hemisphere = matcher.group(1).toUpperCase(); + int degrees = Integer.parseInt(matcher.group(2)); + int minutes = Integer.parseInt(matcher.group(3)); + int seconds = Integer.parseInt(matcher.group(4)); + double coordinate = degrees + (minutes / 60.0) + (seconds / 3600.0); + if (hemisphere.equals("W") || hemisphere.equals("S")) { + coordinate *= -1; + } + return coordinate; + } + return null; +} /** + * Check decimal degree coordinates to make sure they are valid. + * + * @param Double west, east, north, south + * @return boolean + */ +public boolean validateCoordinates(Double west, Double east, Double north, Double south) { + if (west == null || east == null || north == null || south == null) { + return false; + } + if (west > 180.0 || west < -180.0 || east > 180.0 || east < -180.0) { + return false; + } + if (north > 90.0 || north < -90.0 || south > 90.0 || south < -90.0) { + return false; + } + if (north < south || west > east) { + return false; + } + return true; +} + +/** + * THIS FUNCTION HAS BEEN DEPRECATED. * Determine the longitude and latitude of the items location. * * @param Record record @@ -46,10 +263,9 @@ public String getLongLat(Record record) { val = val + ',' + val2; } } - return val; + return val; } } //otherwise return null return null; -} - +} \ No newline at end of file diff --git a/import/marc_local.properties b/import/marc_local.properties index e6f54a1d3bb579896b003403d653f476740c3891..d98bd32dbd6703da3313ff98b527ab98d7284529 100644 --- a/import/marc_local.properties +++ b/import/marc_local.properties @@ -54,9 +54,15 @@ # https://vufind.org/wiki/indexing:full_text_tools #fulltext = custom, getFulltext(856u, pdf) -# Uncomment the following line if you want to index latitude/longitude data for -# Google Map recommendations: -#long_lat = custom, getLongLat +# Uncomment the following if you want to use the OpenLayers3 Geographic Search +# and Google Map or OpenLayers3 Geo-Display functionality +# See searches.ini for configuration options for Geographic Searching. +# See config.ini for configuration options for Geo-Display. +#bbox_geo = custom, getAllCoordinates +#long_lat = custom, getPointCoordinates +#long_lat_display = custom, getDisplayCoordinates +#long_lat_label = 034z + # Uncomment the following lines if you are indexing journal article data that uses # the 773 field to describe the journal containing the article. These settings diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index a375d807c25d18083144a77b5e67846368843cd0..8718109e4a7f6716981ae48605483262ca36c93a 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -422,6 +422,7 @@ $config = [ 'europeanaresults' => 'VuFind\Recommend\Factory::getEuropeanaResults', 'expandfacets' => 'VuFind\Recommend\Factory::getExpandFacets', 'favoritefacets' => 'VuFind\Recommend\Factory::getFavoriteFacets', + 'mapselection' => 'VuFind\Recommend\Factory::getMapSelection', 'resultgooglemapajax' => 'VuFind\Recommend\Factory::getResultGoogleMapAjax', 'sidefacets' => 'VuFind\Recommend\Factory::getSideFacets', 'randomrecommend' => 'VuFind\Recommend\Factory::getRandomRecommend', diff --git a/module/VuFind/src/VuFind/Recommend/Factory.php b/module/VuFind/src/VuFind/Recommend/Factory.php index 6b8bd25320cc1227bc654422b986831844bd252f..23b56b4aec8ca2a05232012058903d40b5824c30 100644 --- a/module/VuFind/src/VuFind/Recommend/Factory.php +++ b/module/VuFind/src/VuFind/Recommend/Factory.php @@ -182,6 +182,21 @@ class Factory ); } + /** + * Factory for MapSelection module. + * + * @param ServiceManager $sm Service manager. + * + * @return MapSelection + */ + public function getMapSelection(ServiceManager $sm) + { + $config = $sm->getServiceLocator()->get('Vufind\Config'); + $backend = $sm->getServiceLocator()->get('VuFind\Search\BackendManager'); + $solr = $backend->get('Solr'); + return new MapSelection($config, $solr); + } + /** * Factory for Random Recommendations. * diff --git a/module/VuFind/src/VuFind/Recommend/MapSelection.php b/module/VuFind/src/VuFind/Recommend/MapSelection.php new file mode 100644 index 0000000000000000000000000000000000000000..6ab056bfd4f16d709792ebbb236703dbd677b0fd --- /dev/null +++ b/module/VuFind/src/VuFind/Recommend/MapSelection.php @@ -0,0 +1,630 @@ +<?php +/** + * MapSelection Recommendations Module + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind2 + * @package Recommendations + * @author Vaclav Rosecky <xrosecky@gmail.com> + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +namespace VuFind\Recommend; + +/** + * MapSelection Recommendations Module + * + * @category VuFind2 + * @package Recommendations + * @author Vaclav Rosecky <xrosecky@gmail.com> + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://vufind.org/wiki/vufind2:recommendation_modules Wiki + */ +class MapSelection implements \VuFind\Recommend\RecommendInterface +{ + /** + * Default coordinates. Order is WENS + * + * @var array + */ + protected $defaultCoordinates = []; + + /** + * The geoField variable name + * + * @var string + */ + protected $geoField = 'bbox_geo'; + + /** + * Height of search map pane + * + * @var string + */ + protected $height; + + /** + * Selected coordinates + * + * @var string + */ + protected $selectedCoordinates = null; + + /** + * Search parameters + * + * @var string + */ + protected $searchParams = null; + + /** + * Search object + * + * @var string + */ + protected $searchObject; + + /** + * Search Results coordinates + * + * @var array + */ + protected $searchResultCoords = []; + + /** + * Bbox search box coordinates + * + * @var array + */ + protected $bboxSearchCoords = []; + + /** + * Configuration loader + * + * @var \VuFind\Config\PluginManager + */ + protected $configLoader; + + /** + * Solr search loader + * + * @var \VuFind\Search\BackendManager + */ + protected $solr; + + /** + * Query Builder object + * + * @var \VuFind\Search\BackendManager + */ + protected $queryBuilder; + + /** + * Solr connector Object + * + * @var \VuFind\Search\BackendManager + */ + protected $solrConnector; + + /** + * Query Object + * + * @var \VuFind\Search\BackendManager + */ + protected $searchQuery; + + /** + * Backend Parameters / Search Filters + * + * @var \VuFind\Search\BackendManager + */ + protected $searchFilters; + + /** + * Constructor + * + * @param \VuFind\Config\PluginManager $configLoader Configuration loader + * @param \VuFind\Search\BackendManager $solr Search interface + */ + public function __construct(\VuFind\Config\PluginManager $configLoader, $solr) + { + $this->configLoader = $configLoader; + $this->solr = $solr; + $this->queryBuilder = $solr->getQueryBuilder(); + $this->solrConnector = $solr->getConnector(); + } + + /** + * SetConfig + * + * Store the configuration of the recommendation module. + * + * @param string $settings Settings from searches.ini. + * + * @return void + */ + public function setConfig($settings) + { + $settings = explode(':', $settings); + $mainSection = empty($settings[0]) ? 'MapSelection' : $settings[0]; + $iniFile = empty($settings[1]) ? 'searches' : $settings[1]; + $config = $this->configLoader->get($iniFile); + if (isset($config->$mainSection)) { + $entries = $config->$mainSection; + if (isset($entries->default_coordinates)) { + $this->defaultCoordinates = explode( + ',', $entries->default_coordinates + ); + } + if (isset($entries->height)) { + $this->height = $entries->height; + } + } + } + + /** + * Init + * + * Called at the end of the Search Params objects' initFromRequest() method. + * This method is responsible for setting search parameters needed by the + * recommendation module and for reading any existing search parameters that may + * be needed. + * + * @param \VuFind\Search\Solr\Params $params Search parameter object + * @param \Zend\StdLib\Parameters $request Parameter object representing user + * request. + * + * @return void + */ + public function init($params, $request) + { + } + + /** + * Process + * + * Called after the Search Results object has performed its main search. This + * may be used to extract necessary information from the Search Results object + * or to perform completely unrelated processing. + * + * @param \VuFind\Search\Base\Results $results Search results object + * + * @return void + */ + public function process($results) + { + $reorder_coords = []; + $filters = $results->getParams()->getFilters(); + foreach ($filters as $key => $value) { + if ($key == $this->geoField) { + $match = []; + if (preg_match( + '/Intersects\(ENVELOPE\((.*), (.*), (.*), (.*)\)\)/', + $value[0], $match + ) + ) { + array_push( + $this->bboxSearchCoords, + (float)$match[1], (float)$match[2], + (float)$match[3], (float)$match[4] + ); + // Need to reorder coords from WENS to WSEN + array_push( + $reorder_coords, + (float)$match[1], (float)$match[4], + (float)$match[2], (float)$match[3] + ); + $this->selectedCoordinates = $reorder_coords; + } + $this->searchParams = $results->getUrlQuery()->removeFacet( + $this->geoField, $value[0], false + ); + } + } + if ($this->searchParams == null) { + $this->searchParams = $results->getUrlQuery()->getParams(false); + } + $this->searchFilters = $results->getParams()->getBackendParameters(); + $this->searchQuery = $results->getParams()->getQuery(); + } + + /** + * GetSelectedCoordinates + * + * Return coordinates selected by user + * + * @return array of floats + */ + public function getSelectedCoordinates() + { + return $this->selectedCoordinates; + } + + /** + * GetDefaultCoordinates + * + * Return default coordinates from configuration + * + * @return array of floats + */ + public function getDefaultCoordinates() + { + return $this->defaultCoordinates; + } + + /** + * GetHeight + * + * Return height of map in pixels + * + * @return number + */ + public function getHeight() + { + return $this->height; + } + + /** + * GetSearchParams + * + * Return search params without filter for geographic search + * + * @return string + */ + public function getSearchParams() + { + return $this->searchParams; + } + + /** + * GetSearchParams no question mark at end + * + * Return search params without leading question mark and colon. + * Copied from ResultGoogleMapAjax.php and chngd name to add NoQ.LMG + * + * @return string + */ + public function getSearchParamsNoQ() + { + // Get search parameters and return them minus the leading ?: + return substr($this->searchObject->getUrlQuery()->getParams(false), 1); + } + + /** + * GetGeoField + * + * Return Solr field to use for geographic search + * + * @return string + */ + public function getGeoField() + { + return $this->geoField; + } + /** + * Get bbox_geo field values for all search results + * + * @return array + */ + public function getSearchResultCoordinates() + { + $result = []; + $params = $this->searchFilters; + // Check to makes sure we have a geographic search + if (strpos($params->get('fq')[0], 'bbox_geo') !== false) { + $params->mergeWith($this->queryBuilder->build($this->searchQuery)); + $params->set('fl', 'id, bbox_geo, title'); + $params->set('wt', 'json'); + $params->set('rows', '10000000'); // set to return all results + $response = json_decode($this->solrConnector->search($params)); + foreach ($response->response->docs as $current) { + $result[] = [$current->id, $current->bbox_geo, $current->title]; + } + } + return $result; + } + + /** + * Convert coordinates to 360 degree grid + * + * @param array $coordinates coordinates for conversion + * + * @return array + */ + public function coordinatesToGrid($coordinates) + { + $gridCoords = []; + list($coordW, $coordE, $coordN, $coordS) = $coordinates; + if ($coordE == (float)-0) { + $coordE = (float)0; + } + // Convert coordinates to 360 degree grid + if ($coordE < $coordW && $coordE < 0) { + $coordE = 360 + $coordE; + } + if ($coordW < 0 && $coordW >= -180) { + $coordW = 360 + $coordW; + $coordE = 360 + $coordE; + } + $gridCoords = [$coordW, $coordE, $coordN, $coordS]; + return $gridCoords; + } + + /** + * Convert coordinates to longitude latitude grid + * + * @param array $centerPt coordinates for conversion + * + * @return array + */ + public function centerToLongLat($centerPt) + { + $LongLatCoords = []; + list($coordWE, $coordSN) = $centerPt; + // convert coordinate to 180 degree grid + if ($coordWE > 180) { + $coordWE = $coordWE - 360; + } + $LongLatCoords = [$coordWE, $coordSN]; + return $LongLatCoords; + } + + /** + * Calculated the center of search box and coordinate overlap + * + * @param array $bboxCoords search box coordinates + * @param array $coordinates coordinates for conversion + * + * @return array + */ + public function getCenterFromBboxCoordIntersect($bboxCoords, $coordinates) + { + $centerCoordBbox = []; + list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; + list($coordW, $coordE, $coordN, $coordS) = $coordinates; + $bboxLon = range(floor($bboxW), ceil($bboxE)); + $bboxLat = range(floor($bboxS), ceil($bboxN)); + $coordLon = range(floor($coordW), ceil($coordE)); + $coordLat = range(floor($coordS), ceil($coordN)); + $cbLon = array_intersect($coordLon, $bboxLon); + $cbLat = array_intersect($coordLat, $bboxLat); + $centerCoordBbox = [ + min($cbLon), max($cbLon), min($cbLat), max($cbLat) + ]; + return $centerCoordBbox; + } + + /** + * Check to see if coordinate and bbox intersect + * + * @param array $bboxCoords searchbox coordinates + * @param array $coordinate result record coordinates + * + * @return bool + */ + public function coordBboxIntersect($bboxCoords, $coordinate) + { + $coordIntersect = false; + list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; + list($coordW, $coordE, $coordN, $coordS) = $coordinate; + //Does coordinate fall within search box + if ((($coordW >= $bboxW && $coordW <= $bboxE) + || ($coordE >= $bboxW && $coordE <= $bboxE)) + && (($coordS >= $bboxS && $coordS <= $bboxN) + || ($coordN >= $bboxS && $coordN <= $bboxN)) + ) { + $coordIntersect = true; + } + // Does searchbox fall within coordinate + if ((($bboxW >= $coordW && $bboxW <= $coordE) + || ($bboxE >= $coordW && $bboxE <= $coordE)) + && (($bboxS >= $coordS && $bboxS <= $coordN) + || ($bboxN >= $coordS && $bboxN <= $coordN)) + ) { + $coordIntersect = true; + } + // Does searchbox span coordinate + if ((($coordE >= $bboxW && $coordE <= $bboxE) + && ($coordW >= $bboxW && $coordW <= $bboxE)) + && ($coordN > $bboxN && $coordS < $bboxS) + ) { + $coordIntersect = true; + } + // Does coordinate span searchbox + if (($coordW < $bboxW && $coordE > $bboxE) + && (($coordS >= $bboxS && $coordS <= $bboxN) + && ($coordN >= $bboxS && $coordN <= $bboxN)) + ) { + $coordIntersect = true; + } + return $coordIntersect; + } + + /** + * Calculate center point of coordinate set + * + * @param array $coordinate centerPoint coordinate + * + * @return array + */ + public function calculateCenterPoint($coordinate) + { + $centerCoord = []; + list($coordW, $coordE, $coordN, $coordS) = $coordinate; + // Calculate center point + $centerWE = (($coordW - $coordE) / 2) + $coordE; + $centerSN = (($coordN - $coordS) / 2) + $coordS; + // Return WENS coordinates even though W=E and N=S + $centerCoord = [$centerWE, $centerWE, $centerSN, $centerSN]; + return $centerCoord; + } + + /** + * Create array of geo features that are in search box + * + * Return search results record data + * + * @param string $recordId record ID + * @param string $recordCoord record coordinates + * @param string $title record title + * @param array $bboxCoords search box coordinates + * + * @return array + */ + public function createGeoFeature($recordId, $recordCoord, $title, $bboxCoords) + { + $recId = $recordId; + $recCoord = $recordCoord; + $recTitle = $title; + list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; + $centerData = []; + $match = []; + if (preg_match('/ENVELOPE\((.*),(.*),(.*),(.*)\)/', $recCoord, $match)) { + // Convert coordinates to 360 degree grid + $floats = array_map('floatval', $match); + $matchCoords = [$floats[1], $floats[2], $floats[3], $floats[4]]; + $gridCoords = $this->coordinatesToGrid($matchCoords); + list($coordW, $coordE, $coordN, $coordS) = $gridCoords; + + // Adjust coordinates on grid if necessary based on search box + if ($bboxW > 180 && ($coordW > 0 && $coordW < 180)) { + $coordW = 360 + $coordW; + $coordE = 360 + $coordE; + } + if ($bboxE > 180 && ($coordE > 0 && $coordE < 180)) { + $coordE = 360 + $coordE; + } + //Does coordinate fall within search box + if ($this->coordBboxIntersect( + $bboxCoords, [$coordW, $coordE, $coordN, $coordS] + ) + ) { + // Calculate center point + $centerPt = $this->calculateCenterPoint( + [$coordW, $coordE, $coordN, $coordS] + ); + // Does center point intersect search box? + if ($this->coordBboxIntersect($bboxCoords, $centerPt)) { + // Convert center point to long lat cooridnate + $ctrLongLat = $this->centerToLongLat( + [$centerPt[0], $centerPt[2]] + ); + $centerData = [ + $recId, $ctrLongLat[0], $ctrLongLat[1], $recTitle + ]; + } else { + // Recalculate center point + $centerCoordBbox = $this->getCenterFromBboxCoordIntersect( + [$bboxW, $bboxE, $bboxN, $bboxS], + [$coordW, $coordE, $coordN, $coordS] + ); + // Calculate new center point + $newCtr = $this->calculateCenterPoint($centerCoordBbox); + // Does new center point intersect search box? + if ($this->coordBboxIntersect($bboxCoords, $newCtr)) { + // Convert new center point to long lat cooridnate + $ctrLongLat = $this->centerToLongLat( + [$newCtr[0], $newCtr[2]] + ); + $centerData = [ + $recId, $ctrLongLat[0], $ctrLongLat[1], $recTitle + ]; + } else { + // Make center point center of search box + $bboxCtr = $this->calculateCenterPoint( + [$bboxW, $bboxE, $bboxN, $bboxS] + ); + $ctrLongLat = $this->centerToLongLat( + [$bboxCtr[0],$bboxCtr[2]] + ); + $centerData = [ + $recId, $ctrLongLat[0], $ctrLongLat[1], $recTitle + ]; + } + + } + } + } + return $centerData; + } + + /** + * Process search result record coordinate values + * + * Return search results record coordinates and process for + * display on search map. + * + * @return array + */ + public function getMapResultCoordinates() + { + $centerCoords = []; + $rawCoordIds = []; + $centerCoordIds = []; + // Both coordinate variables are in WENS order // + $rawCoords = $this->getSearchResultCoordinates(); + // Convert bbox coords to 360 grid // + $bboxCoords = $this->coordinatesToGrid($this->bboxSearchCoords); + list($bboxW, $bboxE, $bboxN, $bboxS) = $bboxCoords; + foreach ($rawCoords as $idCoords) { + foreach ($idCoords[1] as $coord) { + $recId = $idCoords[0]; + $rawCoordIds[] = $recId; + $title = $idCoords[2]; + $centerPoint = $this->createGeoFeature( + $recId, $coord, $title, $bboxCoords + ); + if ($centerPoint) { + $centerCoordIds[] = $centerPoint[0]; + $centerCoords[] = $centerPoint; + break; + } + } + } + //Solr search includes close-by geo features + //Check and add these if there are any + $addIds = array_merge( + array_diff($rawCoordIds, $centerCoordIds), + array_diff($centerCoordIds, $rawCoordIds) + ); + //Remove duplicate ids + $addIds = array_unique($addIds); + if (count($addIds) > 0) { + $bboxCenter = $this->calculateCenterPoint( + [$bboxW, $bboxE, $bboxN, $bboxS] + ); + $centerLongLat = $this->centerToLongLat([$bboxCenter[0],$bboxCenter[2]]); + foreach ($addIds as $coordId) { + foreach ($rawCoords as $idCoords) { + if ($coordId == $idCoords[0]) { + $title = $idCoords[2]; + $centerCoords[] = [$coordId, + $centerLongLat[0], + $centerLongLat[1], + $title + ]; + } + } + } + } + return $centerCoords; + } +} diff --git a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php index 988f4fb5495a68b471f6efb570c82ed85c446b88..12c8fa6e561f7dfe5dcb00aa34d4a31e93e37152 100644 --- a/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php +++ b/module/VuFind/src/VuFind/RecordDriver/SolrDefault.php @@ -1799,12 +1799,12 @@ class SolrDefault extends AbstractBase /** * Get longitude/latitude text (or false if not available). * - * @return string|bool + * @return array */ public function getLongLat() { return isset($this->fields['long_lat']) - ? $this->fields['long_lat'] : false; + ? $this->fields['long_lat'] : []; } /** @@ -1914,4 +1914,37 @@ class SolrDefault extends AbstractBase && !empty($this->fields['hierarchy_parent_id']) ? $this->fields['hierarchy_parent_id'][0] : ''; } + + /** + * Get the bbox-geo variable. + * + * @return array + */ + public function getBbox() + { + return isset($this->fields['bbox_geo']) + ? $this->fields['bbox_geo'] : []; + } + + /** + * Get the map display (lat/lon) coordinates + * + * @return array + */ + public function getDisplayCoordinates() + { + return isset($this->fields['long_lat_display']) + ? $this->fields['long_lat_display'] : []; + } + + /** + * Get the map display (lat/lon) labels + * + * @return array + */ + public function getCoordinateLabels() + { + return isset($this->fields['long_lat_label']) + ? $this->fields['long_lat_label'] : []; + } } diff --git a/module/VuFind/src/VuFind/RecordTab/Factory.php b/module/VuFind/src/VuFind/RecordTab/Factory.php index 82e6fa835cc4deae6d1a31ccc2f15ecf1680599d..f37187e7cc78f947df08acd900dae1939cb0310d 100644 --- a/module/VuFind/src/VuFind/RecordTab/Factory.php +++ b/module/VuFind/src/VuFind/RecordTab/Factory.php @@ -181,7 +181,7 @@ class Factory ? $config->Content->recordMap : null; $options = []; $optionFields = [ - 'googleMapApiKey' + 'displayCoords', 'mapLabels', 'googleMapApiKey' ]; foreach ($optionFields as $field) { if (isset($config->Content->$field)) { diff --git a/module/VuFind/src/VuFind/RecordTab/Map.php b/module/VuFind/src/VuFind/RecordTab/Map.php index fbfae2c162c779b84ec4388a7c8ccd2357795b77..8a12adf1a2697a8b465fe2ab418487caab512b0e 100644 --- a/module/VuFind/src/VuFind/RecordTab/Map.php +++ b/module/VuFind/src/VuFind/RecordTab/Map.php @@ -22,6 +22,7 @@ * @category VuFind * @package RecordTabs * @author Demian Katz <demian.katz@villanova.edu> + * @author Leila Gonzales <lmg@agiweb.org> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ @@ -33,6 +34,7 @@ namespace VuFind\RecordTab; * @category VuFind * @package RecordTabs * @author Demian Katz <demian.katz@villanova.edu> + * @author Leila Gonzales <lmg@agiweb.org> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki */ @@ -45,6 +47,20 @@ class Map extends AbstractBase */ protected $mapType = null; + /** + * Should we display coordinates as part of labels? + * + * @var bool + */ + protected $displayCoords = false; + + /** + * Map labels setting from config.ini. + * + * @var string + */ + protected $mapLabels = null; + /** * Google Maps API key. * @@ -69,7 +85,14 @@ class Map extends AbstractBase throw new \Exception('Google API key must be set in config.ini'); } $this->googleMapApiKey = $options['googleMapApiKey']; + case 'openlayers': $this->mapType = trim(strtolower($mapType)); + $legalOptions = ['displayCoords', 'mapLabels']; + foreach ($legalOptions as $option) { + if (isset($options[$option])) { + $this->$option = $options[$option]; + } + } break; } } @@ -81,7 +104,7 @@ class Map extends AbstractBase */ public function supportsAjax() { - // No, Google script magic required + // No, magic required return false; } @@ -95,28 +118,6 @@ class Map extends AbstractBase return 'Map View'; } - /** - * Get the JSON needed to display the record on a Google map. - * - * @return string - */ - public function getGoogleMapMarker() - { - $longLat = $this->getRecordDriver()->tryMethod('getLongLat'); - if (empty($longLat)) { - return json_encode([]); - } - $longLat = explode(',', $longLat); - $markers = [ - [ - 'title' => (string) $this->getRecordDriver()->getBreadcrumb(), - 'lon' => $longLat[0], - 'lat' => $longLat[1] - ] - ]; - return json_encode($markers); - } - /** * Get the map type for determining template to use. * @@ -144,10 +145,187 @@ class Map extends AbstractBase */ public function isActive() { - if ($this->mapType == 'google') { + if ($this->mapType == 'openlayers') { + $geocoords = $this->getRecordDriver()->tryMethod('getBbox'); + return !empty($geocoords); + } else if ($this->mapType == 'google') { $longLat = $this->getRecordDriver()->tryMethod('getLongLat'); return !empty($longLat); } return false; } + + /** + * Get the JSON needed to display the record on a Google map. + * + * @return string + */ + public function getGoogleMapMarker() + { + $longLat = $this->getRecordDriver()->tryMethod('getLongLat'); + if (empty($longLat)) { + return json_encode([]); + } + $markers = []; + $mapDisplayLabels = $this->getMapLabels(); + foreach ($longLat as $key => $value) { + $coordval = explode(',', $value); + $label = isset($mapDisplayLabels[$key]) + ? $mapDisplayLabels[$key] : ''; + $markers[] = [ + [ + 'title' => $label, + 'lon' => $coordval[0], + 'lat' => $coordval[1] + ] + ]; + } + return json_encode($markers); + } + + /** + * Get the bbox-geo coordinates. + * + * @return array + */ + public function getBboxCoords() + { + $geoCoords = $this->getRecordDriver()->tryMethod('getBbox'); + if (empty($geoCoords)) { + return []; + } + $coordarray = []; + /* Extract coordinates from bbox_geo field */ + foreach ($geoCoords as $key => $value) { + $match = []; + if (preg_match('/ENVELOPE\((.*),(.*),(.*),(.*)\)/', $value, $match)) { + $lonW = (float)$match[1]; + $lonE = (float)$match[2]; + $latN = (float)$match[3]; + $latS = (float)$match[4]; + // Display as point or polygon? + if (($lonE == $lonW) && ($latN == $latS)) { + $shape = 2; + } else { + $shape = 4; + } + // Coordinates ordered for ol3 display as WSEN + array_push($coordarray, [$lonW, $latS, $lonE, $latN, $shape]); + } + } + return $coordarray; + } + + /** + * Get the map display coordinates. + * + * @return array + */ + public function getDisplayCoords() + { + $label_coords = []; + $coords = $this->getRecordDriver()->tryMethod('getDisplayCoordinates'); + foreach ($coords as $val) { + $coord = explode(' ', $val); + $labelW = $coord[0]; + $labelE = $coord[1]; + $labelN = $coord[2]; + $labelS = $coord[3]; + /* Create coordinate label for map display */ + if (($labelW == $labelE) && ($labelN == $labelS)) { + $labelcoord = $labelS . ' ' . $labelE; + } else { + /* Coordinate order is min to max on lat and long axes */ + $labelcoord = $labelS . ' ' . $labelN . ' ' . + $labelW . ' ' . $labelE; + } + array_push($label_coords, $labelcoord); + } + return $label_coords; + } + + /** + * Get the map labels. + * + * @return array + */ + public function getMapLabels() + { + $labels = []; + $mapLabelData = explode(':', $this->mapLabels); + if ($mapLabelData[0] == 'driver') { + $labels = $this->getRecordDriver()->tryMethod('getCoordinateLabels'); + return $labels; + } + if ($mapLabelData[0] == 'file') { + $coords = $this->getRecordDriver()->tryMethod('getDisplayCoordinates'); + /* read lookup file into array */ + $label_lookup = []; + $file = \VuFind\Config\Locator::getConfigPath($mapLabelData[1]); + if (file_exists($file)) { + $fp = fopen($file, 'r'); + while (($line = fgetcsv($fp, 0, "\t")) !== false) { + if (count($line) > 1) { + $label_lookup[$line[0]] = $line[1]; + } + } + fclose($fp); + } + $labels = []; + if (null !== $coords) { + foreach ($coords as $val) { + /* Collapse spaces to make combined coordinate string to match + against lookup table coordinate */ + $coordmatch = implode('', explode(' ', $val)); + /* See if coordinate string matches lookup + table coordinates and if so return label */ + $labelname = isset($label_lookup[$coordmatch]) + ? $label_lookup[$coordmatch] : ''; + array_push($labels, $labelname); + } + } + return $labels; + } + } + + /** + * Construct the map coordinates and labels array. + * + * @return array + */ + public function getMapTabData() + { + $geoCoords = $this->getBboxCoords(); + if (empty($geoCoords)) { + return []; + } + $mapTabData = []; + $mapDisplayCoords = []; + $mapDisplayLabels = []; + if ($this->displayCoords) { + $mapDisplayCoords = $this->getDisplayCoords(); + } + if (isset($this->mapLabels)) { + $mapDisplayLabels = $this->getMapLabels(); + } + // Pass coordinates, display coordinates, and labels + foreach ($geoCoords as $key => $value) { + $mapCoords = ''; + $mapLabel = ''; + if ($this->displayCoords) { + $mapCoords = $mapDisplayCoords[$key]; + } + if (isset($this->mapLabels)) { + $mapLabel = $mapDisplayLabels[$key]; + } + array_push( + $mapTabData, [ + $geoCoords[$key][0], $geoCoords[$key][1], + $geoCoords[$key][2], $geoCoords[$key][3], + $geoCoords[$key][4], $mapLabel, $mapCoords + ] + ); + } + return $mapTabData; + } } diff --git a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php b/module/VuFind/src/VuFind/View/Helper/Root/Factory.php index 39a95589a684b21dc91e24cf600be99d80435454..340d1b8154ffc28ba3e5121b5a6f8e13b59bdc5b 100644 --- a/module/VuFind/src/VuFind/View/Helper/Root/Factory.php +++ b/module/VuFind/src/VuFind/View/Helper/Root/Factory.php @@ -202,6 +202,21 @@ class Factory return new Flashmessages($messenger); } + /** + * Construct the GeoCoords helper. + * + * @param ServiceManager $sm Service manager. + * + * @return GeoCoords + */ + public static function getGeoCoords(ServiceManager $sm) + { + $config = $sm->getServiceLocator()->get('VuFind\Config')->get('searches'); + $coords = isset($config->MapSelection->default_coordinates) + ? $config->MapSelection->default_coordinates : false; + return new GeoCoords($coords); + } + /** * Construct the GoogleAnalytics helper. * diff --git a/module/VuFind/src/VuFind/View/Helper/Root/GeoCoords.php b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoords.php new file mode 100644 index 0000000000000000000000000000000000000000..dcc3e54cbccafd07a6c0d6db067759a03039704d --- /dev/null +++ b/module/VuFind/src/VuFind/View/Helper/Root/GeoCoords.php @@ -0,0 +1,115 @@ +<?php +/** + * GeoCoords view helper + * + * PHP version 5 + * + * Copyright (C) Villanova University 2010. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package View_Helpers + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Site + */ +namespace VuFind\View\Helper\Root; +use VuFind\Search\Base\Options; + +/** + * GeoCoords view helper + * + * @category VuFind + * @package View_Helpers + * @author Leila Gonzales <lmg@agiweb.org> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class GeoCoords extends \Zend\View\Helper\AbstractHelper +{ + /** + * Is Map Search enabled? + * + * @var bool + */ + protected $enabled; + + /** + * Default coordinates + * + * @var string + */ + protected $coords; + + /** + * Get geoField variable name + * + * @var string + */ + protected $geoField = 'bbox_geo'; + + /** + * Constructor + * + * @param string $coords Default coordinates + */ + public function __construct($coords) + { + $this->coords = $coords; + } + + /** + * Check if the relevant recommendation module is enabled; if not, there is no + * point in generating a search link. Note that right now we are assuming it is + * set up as a default top recommendation; this may need to be made more + * flexible in future to account for more use cases. + * + * @param array $settings Recommendation settings + * + * @return bool + */ + protected function recommendationEnabled($settings) + { + if (isset($settings['top'])) { + foreach ($settings['top'] as $setting) { + $parts = explode(':', $setting); + if (strtolower($parts[0]) === 'mapselection') { + return true; + } + } + } + return false; + } + + /** + * Get search URL if geo search is enabled for the specified search class ID, + * false if disabled. + * + * @param Options $options Search options + * + * @return string|bool + */ + public function getSearchUrl(Options $options) + { + // If the relevant module is disabled, bail out now: + if (!$this->recommendationEnabled($options->getRecommendationSettings())) { + return false; + } + $urlHelper = $this->getView()->plugin('url'); + return $urlHelper('search-results') + . '?filter[]=' . urlencode($this->geoField) + . ':Intersects(ENVELOPE(' . urlencode($this->coords) . '))'; + } +} diff --git a/solr/vufind/biblio/conf/schema.xml b/solr/vufind/biblio/conf/schema.xml index be8d0abde3a4c0b5361ece675b53b674a9342b32..2c326ee8935af5fdc067742d71111fb3b8d09bbb 100644 --- a/solr/vufind/biblio/conf/schema.xml +++ b/solr/vufind/biblio/conf/schema.xml @@ -98,6 +98,8 @@ <fieldType name="date" class="solr.TrieDateField" sortMissingLast="true" omitNorms="true" precisionStep="6"/> <fieldType name="random" class="solr.RandomSortField" indexed="true" /> <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true" omitNorms="true"/> + <!-- add geo field to handle geographic search and display capabilities --> + <fieldType name="geo" class="solr.SpatialRecursivePrefixTreeFieldType" distErrPct="0.025" maxDistErr="0.000009" distanceUnits="degrees" /> </types> <fields> <!-- Required by Solr 4.x --> @@ -187,7 +189,11 @@ <field name="era" type="text" indexed="true" stored="true" multiValued="true"/> <field name="era_facet" type="textFacet" indexed="true" stored="true" multiValued="true"/> <field name="illustrated" type="string" indexed="true" stored="true" multiValued="false"/> - <field name="long_lat" type="textFacet" indexed="true" stored="true" multiValued="false"/> + <!-- Used for geographic map display --> + <!-- long_lat only used for Google Maps display of point features --> + <field name="long_lat" type="textFacet" indexed="true" stored="true" multiValued="true"/> + <field name="long_lat_display" type="text" indexed="true" stored="true" multiValued="true"/> + <field name="long_lat_label" type="string" indexed="false" stored="true" multiValued="true"/> <!-- Container fields (i.e. for describing journal containing an article) --> <field name="container_title" type="text" indexed="true" stored="true"/> <field name="container_volume" type="text" indexed="true" stored="true"/> @@ -225,6 +231,8 @@ <dynamicField name="*_txtP_mv" type="textProper" indexed="true" stored="true" multiValued="true"/> <dynamicField name="*_random" type="random" /> <dynamicField name="*_boolean" type="boolean" indexed="true" stored="true"/> + <!-- add geo field to handle geographic search and display capabilities --> + <dynamicField name="*_geo" type="geo" indexed="true" stored="true" multiValued="true" /> </fields> <uniqueKey>id</uniqueKey> <defaultSearchField>allfields</defaultSearchField> diff --git a/tests/data/geo.mrc b/tests/data/geo.mrc new file mode 100644 index 0000000000000000000000000000000000000000..42f35e0e55684076d1095dc77fac3758c6666e54 --- /dev/null +++ b/tests/data/geo.mrc @@ -0,0 +1 @@ +00362naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007000138100001700208245002700225 2000120160513104939730000s1973 coAa 0 0 EL d0 aad+129.95348029e+129.95348029f-55.29356577g-55.29356577zSite 3740 aad+92.58856498e+92.58856498f+35.0285559g+35.0285559zSite 4511 aHurt, Millie10aTest Publication 2000100364naaa 2200109zu 4500001000800000005001500008008004100023034007000064034007200134100002100206245002700227 2000220160513104939730000s1973 coAa 0 0 EL d0 aad+10.05908843e+10.05908843f-5.75031466g-5.75031466zSite 4400 aad+46.45283977e+46.45283977f+31.50720239g+31.50720239zSite 4471 aWinrow, Sanjuana10aTest Publication 2000200360naaa 2200109zu 4500001000800000005001500008008004100023034007200064034007200136100001500208245002700223 2000320160513104939730000s1973 coAa 0 0 EL d0 aad-35.26826575e-35.26826575f+27.26620523g+27.26620523zSite 4000 aad-15.10285454e-15.10285454f+14.80524368g+14.80524368zSite 1631 aRudd, Jann10aTest Publication 2000300613naaa 2200145zu 4500001000800000005001500008008004100023034007200064034007200136034006600208034007400274034007200348100002000420245002700440 2000420160513104939730000s1973 coAa 0 0 EL d0 aad-56.86690649e-56.86690649f+15.77554895g+15.77554895zSite 4790 aad+159.74574799e+159.74574799f-20.9661451g-20.9661451zSite 4340 aad+7.9676261e+7.9676261f+6.62658415g+6.62658415zSite 4480 aad+135.37478775e+135.37478775f+46.27024658g+46.27024658zSite 5530 aad-105.9279514e-105.9279514f+35.25445142g+35.25445142zSite 3061 aWalston, Verlie10aTest Publication 2000400526naaa 2200133zu 4500001000800000005001500008008004100023034007200064034007200136034006600208034007200274100001900346245002700365 2000520160513104939730000s1973 coAa 0 0 EL d0 aad+83.63104693e+83.63104693f+21.46845472g+21.46845472zSite 3140 aad+156.17691966e+156.17691966f-9.22226407g-9.22226407zSite 1400 aad-7.6873641e-7.6873641f+49.3997425g+49.3997425zSite 1050 aad-85.11852582e-85.11852582f-32.54764684g-32.54764684zSite 2661 aPakele, Marina10aTest Publication 2000500365naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007000138100002000208245002700228 2000620160513104939730000s1973 coAa 0 0 EL d0 aadE119.53888243eE119.53888243fN62.91327942gN62.91327942zSite 2550 aadE67.8057745eE67.8057745fN36.44414008gN36.44414008zSite 5461 aMcglasson, Rich10aTest Publication 2000600627naaa 2200145zu 4500001000800000005001500008008004100023034007400064034007400138034007200212034007400284034007400358100002200432245002700454 2000720160513104939730000s1973 coAa 0 0 EL d0 aadE121.59566053eE121.59566053fN23.86811097gN23.86811097zSite 3610 aadE151.51785078eE151.51785078fN13.91875447gN13.91875447zSite 1050 aadW86.72014286eW86.72014286fS10.73770388gS10.73770388zSite 4880 aadE134.89543887eE134.89543887fN25.94241103gN25.94241103zSite 2060 aadE161.78292166eE161.78292166fN10.15327259gN10.15327259zSite 3501 aRottman, Serafina10aTest Publication 2000700621naaa 2200145zu 4500001000800000005001500008008004100023034007400064034007400138034007200212034007200284034007200356100002000428245002700448 2000820160513104939730000s1973 coAa 0 0 EL d0 aadW103.57340238eW103.57340238fS22.78804289gS22.78804289zSite 2170 aadW175.68166135eW175.68166135fS69.41361345gS69.41361345zSite 4970 aadE48.51599542eE48.51599542fS58.33629524gS58.33629524zSite 3550 aadE65.04196038eE65.04196038fN32.99243302gN32.99243302zSite 4860 aadW111.45384173eW111.45384173fS7.22546191gS7.22546191zSite 1131 aHaberle, Manuel10aTest Publication 2000800616naaa 2200145zu 4500001000800000005001500008008004100023034007000064034007000134034007400204034007200278034007000350100002300420245002700443 2000920160513104939730000s1973 coAa 0 0 EL d0 aadE1.55144883eE1.55144883fS15.91268613gS15.91268613zSite 4710 aadE0.46771867eE0.46771867fS53.88776866gS53.88776866zSite 1390 aadE120.96526676eE120.96526676fN22.40092028gN22.40092028zSite 2390 aadW43.86881638eW43.86881638fS29.38831583gS29.38831583zSite 1970 aadW158.26944205eW158.26944205fS0.7378453gS0.7378453zSite 3941 aRuvalcaba, Letisha10aTest Publication 2000900326naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001700172245002700189 2001020160513104939730000s1973 coAa 0 0 EL d0 aadE0870709eE0870709fS065613gS065613zSite 1200 aadE1242258eE1242258fS110713gS110713zSite 2231 aBogard, Noma10aTest Publication 2001000525naaa 2200145zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226034005400280100001800334245002700352 2001120160513104939730000s1973 coAa 0 0 EL d0 aadE0025331eE0025331fS493930gS493930zSite 3010 aadE0013038eE0013038fN444623gN444623zSite 1560 aadW0483141eW0483141fS452235gS452235zSite 4630 aadW0703406eW0703406fS373129gS373129zSite 3630 aadW1172630eW1172630fN661503gN661503zSite 3931 aPutman, Davis10aTest Publication 2001100526naaa 2200145zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226034005400280100001900334245002700353 2001220160513104939730000s1973 coAa 0 0 EL d0 aadE1741450eE1741450fN154310gN154310zSite 5490 aadW0570821eW0570821fS415337gS415337zSite 2360 aadE1773647eE1773647fS330258gS330258zSite 4620 aadE0710915eE0710915fN400408gN400408zSite 3550 aadW0401638eW0401638fN093759gN093759zSite 5471 aBucher, Margit10aTest Publication 2001200528naaa 2200145zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226034005400280100002100334245002700355 2001320160513104939730000s1973 coAa 0 0 EL d0 aadW1511513eW1511513fS055042gS055042zSite 1050 aadW1465931eW1465931fS261609gS261609zSite 4840 aadW0790538eW0790538fS512156gS512156zSite 5440 aadW1415343eW1415343fS185901gS185901zSite 4230 aadW1304431eW1304431fS502042gS502042zSite 1291 aDobyns, Wilfredo10aTest Publication 2001300463naaa 2200133zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226100002200280245002700302 2001420160513104939730000s1973 coAa 0 0 EL d0 aadE1742335eE1742335fN191726gN191726zSite 2130 aadW1570705eW1570705fN435341gN435341zSite 5090 aadW1020233eW1020233fN203933gN203933zSite 2910 aadW1290413eW1290413fS691417gS691417zSite 2251 aMilbourne, Milton10aTest Publication 2001400328naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001900172245002700191 2001520160513104939730000s1973 coAa 0 0 EL d0 aadW0755517eW0755517fN151326gN151326zSite 3270 aadW0932259eW0932259fN020021gN020021zSite 1521 aMirsky, Dionna10aTest Publication 2001500536naaa 2200133zu 4500001000800000005001500008008004100023034007400064034007400138034007100212034007200283100002000355245002700375 2001620160513104939730000s1973 coAa 0 0 EL d0 aad+100.64194149e+150.64194149f+76.29679349g+46.29679349zSite 3250 aad+140.41485857e+150.41485857f-59.57999928g-69.57999928zSite 1980 aad-14.23933962e-9.23933962f-27.72727724g-57.72727724zSite 3170 aad-175.1864435e-172.1864435f-10.03581345g-20.03581345zSite 1721 aInnocent, Leisa10aTest Publication 2001600615naaa 2200145zu 4500001000800000005001500008008004100023034006800064034007100132034007200203034007200275034007200347100002300419245002700442 2001720160513104939730000s1973 coAa 0 0 EL d0 aad-165.6004414e-115.6004414f-14.509653g-24.509653zSite 2190 aad-172.2069375e-132.2069375f+10.76133978g+5.76133978zSite 2500 aad-162.8898108e-142.8898108f-15.07237354g-25.07237354zSite 5040 aad+71.03685744e+81.03685744f-66.09831516g-76.09831516zSite 1810 aad-160.1490595e-140.1490595f+68.15731704g+58.15731704zSite 4201 aCornforth, Desiree10aTest Publication 2001700613naaa 2200145zu 4500001000800000005001500008008004100023034007000064034007200134034007000206034007200276034007300348100001900421245002700440 2001820160513104939730000s1973 coAa 0 0 EL d0 aad-91.5895638e-61.5895638f+74.14618546g+44.14618546zSite 1140 aad+64.41336345e+74.41336345f+79.74325176g+49.74325176zSite 1270 aad-7.44081494e-2.44081494f+68.18607023g+58.18607023zSite 1210 aad-170.1177443e-130.1177443f-63.43626229g-73.43626229zSite 4440 aad+95.39073945e+145.39073945f+42.45583955g+22.45583955zSite 2461 aKarter, Marvis10aTest Publication 2001800441naaa 2200121zu 4500001000800000005001500008008004100023034006800064034007000132034007200202100001800274245002700292 2001920160513104939730000s1973 coAa 0 0 EL d0 aad+10.5670584e+15.5670584f-21.6083379g-41.6083379zSite 2070 aad+41.53787448e+51.53787448f+77.9847143g+57.9847143zSite 5480 aad+129.0839142e+139.0839142f+78.85096117g+68.85096117zSite 2721 aClaro, Bobbie10aTest Publication 2001900620naaa 2200145zu 4500001000800000005001500008008004100023034007400064034006900138034007200207034007400279034007400353100002000427245002700447 2002020160513104939730000s1973 coAa 0 0 EL d0 aadW163.70412537eW143.70412537fN23.98046062gN13.98046062zSite 2460 aadW37.45857716eW27.45857716fN11.7990193gN6.7990193zSite 4110 aadW92.34562344eW62.34562344fS25.81496714gS55.81496714zSite 4880 aadE144.53537717eE174.53537717fS28.13599649gS58.13599649zSite 5160 aadW177.82278613eW117.82278613fS32.33211767gS62.33211767zSite 2831 aBeckett, Tamala10aTest Publication 2002000364naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007200138100001700210245002700227 2002120160513104939730000s1973 coAa 0 0 EL d0 aadE114.22094565eE144.22094565fS37.22983935gS77.22983935zSite 4160 aadW167.75647806eW147.75647806fN8.67140253gN3.67140253zSite 3961 aLam, Cristen10aTest Publication 2002100528naaa 2200133zu 4500001000800000005001500008008004100023034007200064034007200136034007200208034007000280100001700350245002700367 2002220160513104939730000s1973 coAa 0 0 EL d0 aadW97.44010063eW67.44010063fN69.57202273gN39.57202273zSite 3650 aadW79.40943037eW59.40943037fN63.76345804gN43.76345804zSite 2300 aadW103.46816014eW73.46816014fS9.60188743gS14.60188743zSite 2350 aadW16.35276413eW11.35276413fS4.27200773gS9.27200773zSite 2451 aDalke, Isiah10aTest Publication 2002200618naaa 2200145zu 4500001000800000005001500008008004100023034007400064034007200138034007400210034007200284034007400356100001500430245002700445 2002320160513104939730000s1973 coAa 0 0 EL d0 aadE127.23972147eE137.23972147fN63.12317768gN43.12317768zSite 2080 aadE129.15557267eE149.15557267fN45.7443194gN25.7443194zSite 1020 aadW151.73434552eW101.73434552fS55.73496457gS75.73496457zSite 1080 aadE54.94955124eE64.94955124fS34.71609174gS64.71609174zSite 5370 aadE106.81774118eE156.81774118fS29.05905321gS59.05905321zSite 4111 aOram, Donn10aTest Publication 2002300367naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007100138100002100209245002700230 2002420160513104939730000s1973 coAa 0 0 EL d0 aadE129.86022987eE149.86022987fN51.68020143gN31.68020143zSite 4760 aadE5.86815575eE15.86815575fN60.95617804gN40.95617804zSite 1761 aRupp, Georgeanna10aTest Publication 2002400450naaa 2200121zu 4500001000800000005001500008008004100023034007400064034007200138034007200210100001900282245002700301 2002520160513104939730000s1973 coAa 0 0 EL d0 aadW156.11673622eW106.11673622fN78.32587133gN68.32587133zSite 3370 aadE47.92289733eE57.92289733fN77.17739015gN67.17739015zSite 1040 aadW32.89273609eW22.89273609fS40.70960814gS60.70960814zSite 4531 aWalter, Jasper10aTest Publication 2002500532naaa 2200133zu 4500001000800000005001500008008004100023034007300064034007400137034007300211034006800284100001900352245002700371 2002620160513104939730000s1973 coAa 0 0 EL d0 aadE96.70798372eE146.70798372fN54.00011075gN34.00011075zSite 2710 aadE145.51684204eE165.51684204fN27.98976019gN17.98976019zSite 2660 aadW106.32772299eW76.32772299fN46.24201475gN26.24201475zSite 2860 aadE20.1511411eE25.1511411fN6.07910182gN1.07910182zSite 2421 aHultgren, Josh10aTest Publication 2002600328naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001900172245002700191 2002720160513104939730000s1973 coAa 0 0 EL d0 aadW0170403eW0120403fN755211gN455211zSite 4560 aadW1740209eW1340209fN794749gN694749zSite 3851 aGalasso, Tatum10aTest Publication 2002700333naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100002400172245002700196 2002820160513104939730000s1973 coAa 0 0 EL d0 aadE0710106eE0810106fS111308gS211308zSite 5310 aadE0132703eE0182703fN541236gN341236zSite 3481 aCantrell, Cornelius10aTest Publication 2002800399naaa 2200121zu 4500001000800000005001500008008004100023034005400064034005400118034005400172100002400226245002700250 2002920160513104939730000s1973 coAa 0 0 EL d0 aadW1693838eW1193838fN122531gN072531zSite 4150 aadE0844331eE1044331fS693723gS793723zSite 1320 aadE0062611eE0162611fN681512gN381512zSite 5031 aGreenblatt, Sherill10aTest Publication 2002900327naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001800172245002700190 2003020160513104939730000s1973 coAa 0 0 EL d0 aadE1053451eE1553451fS183256gS383256zSite 2870 aadE0830412eE1030412fS174644gS374644zSite 1801 aPerine, Alisa10aTest Publication 2003000393naaa 2200121zu 4500001000800000005001500008008004100023034005400064034005400118034005400172100001800226245002700244 2003120160513104939730000s1973 coAa 0 0 EL d0 aadE0202657eE0252657fS470042gS770042zSite 2360 aadW1594422eW1094422fS562802gS762802zSite 4950 aadW0254325eW0204325fS591429gS691429zSite 3241 aSuen, Lanette10aTest Publication 2003100460naaa 2200133zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226100001900280245002700299 2003220160513104939730000s1973 coAa 0 0 EL d0 aadW1331944eW0931944fN402114gN202114zSite 3060 aadW1780423eW1700423fN584511gN284511zSite 4340 aadW1600712eW1500712fN454326gN254326zSite 3810 aadW1752336eW1652336fN731737gN631737zSite 5211 aIverson, Beryl10aTest Publication 2003200459naaa 2200133zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226100001800280245002700298 2003320160513104939730000s1973 coAa 0 0 EL d0 aadE1423151eE1523151fS521748gS721748zSite 2580 aadW1691508eW1391508fS131306gS231306zSite 5220 aadE1345235eE1745235fS630056gS730056zSite 3540 aadW1640324eW1140324fS330005gS630005zSite 1441 aBoyland, Bebe10aTest Publication 2003300394naaa 2200121zu 4500001000800000005001500008008004100023034005400064034005400118034005400172100001900226245002700245 2003420160513104939730000s1973 coAa 0 0 EL d0 aadE1590126eE1690126fS341136gS641136zSite 3030 aadW1054117eW0754117fS354552gS754552zSite 3730 aadW0994125eW0694125fS434119gS634119zSite 3551 aArechiga, Moon10aTest Publication 2003400457naaa 2200133zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226100001600280245002700296 2003520160513104939730000s1973 coAa 0 0 EL d0 aadW1405617eW1205617fS555856gS755856zSite 4800 aadE1073429eE1573429fS022601gS072601zSite 3730 aadE0913431eE1011343fS422933gS622933zSite 5360 aadE1131855eE1231855fS682514gS782514zSite 3241 aRozar, Josh10aTest Publication 2003500521naaa 2200133zu 4500001000800000005001500008008004100023034006800064034007200132034006800204034007000272100001800342245002700360 2003620160513104939730000s1973 coAa 0 0 EL d0 aad-169.415256e-169.415256f+29.8815696g+29.8815696zSite 5600 aad-151.5560527e-151.5560527f-36.13307132g-36.13307132zSite 2020 aad-87.4479458e-87.4479458f+9.11816125g+9.11816125zSite 5400 aad-149.4183832e-149.4183832f+29.1541857g+29.1541857zSite 5571 aGuynn, Mayola10aTest Publication 2003600366naaa 2200109zu 4500001000800000005001500008008004100023034007200064034007200136100002100208245002700229 2003720160513104939730000s1973 coAa 0 0 EL d0 aad+86.55263228e+86.55263228f-28.95862432g-28.95862432zSite 1680 aad-149.3464366e-129.3464366f+27.28202333g+17.28202333zSite 1341 aEngelhard, Marco10aTest Publication 2003700366naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007200138100001900210245002700229 2003820160513104939730000s1973 coAa 0 0 EL d0 aad+118.90296884e+118.90296884f+27.65389383g+27.65389383zSite 3970 aad-135.3498909e-95.34989087f+60.71524151g+40.71524151zSite 2231 aBornstein, Dia10aTest Publication 2003800363naaa 2200109zu 4500001000800000005001500008008004100023034007000064034007200134100002000206245002700226 2003920160513104939730000s1973 coAa 0 0 EL d0 aad+15.7081159e+15.7081159f+10.51847859g+10.51847859zSite 1540 aad-65.34590317e-45.34590317f-16.22908424g-36.22908424zSite 4341 aMathisen, Dayle10aTest Publication 2003900356naaa 2200109zu 4500001000800000005001500008008004100023034007000064034007000134100001500204245002700219 2004020160513104939730000s1973 coAa 0 0 EL d0 aad+120.07557262e+120.07557262f-7.8300017g-7.8300017zSite 4600 aad-78.33685724e-58.33685724f-20.7677353g-40.7677353zSite 3351 aOrtiz, Tia10aTest Publication 2004000370naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007200138100002300210245002700233 2004120160513104939730000s1973 coAa 0 0 EL d0 aad+179.11624407e+179.11624407f+49.45597061g+49.45597061zSite 1290 aad+18.65135762e+23.65135762f+75.67582839g+65.67582839zSite 3251 aSteffensen, Bianca10aTest Publication 2004100365naaa 2200109zu 4500001000800000005001500008008004100023034007200064034007200136100002000208245002700228 2004220160513104939730000s1973 coAa 0 0 EL d0 aad-69.44473478e-69.44473478f+16.37800971g+16.37800971zSite 5180 aad-31.50294667e-21.50294667f-31.33882964g-61.33882964zSite 3511 aLaurich, Stuart10aTest Publication 2004200369naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007200138100002200210245002700232 2004320160513104939730000s1973 coAa 0 0 EL d0 aad+154.29238217e+154.29238217f-50.92756335g-50.92756335zSite 2970 aad-66.00381134e-46.00381134f-41.48033844g-61.48033844zSite 1781 aBeveridge, Albina10aTest Publication 2004300367naaa 2200109zu 4500001000800000005001500008008004100023034007000064034007400134100002200208245002700230 2004420160513104939730000s1973 coAa 0 0 EL d0 aadE24.1857034eE24.1857034fS66.87278415gS66.87278415zSite 1290 aadW166.03284101eW156.03284101fN65.01324719gN35.01324719zSite 3991 aLetsinger, Tamiko10aTest Publication 2004400366naaa 2200109zu 4500001000800000005001500008008004100023034007000064034007400134100002100208245002700229 2004520160513104939730000s1973 coAa 0 0 EL d0 aadE25.0293899eE25.0293899fS12.28047561gS12.28047561zSite 5060 aadW169.45170238eW119.45170238fN38.14274122gN18.14274122zSite 1251 aCounter, Modesto10aTest Publication 2004500531naaa 2200133zu 4500001000800000005001500008008004100023034007200064034007200136034007200208034007300280100001700353245002700370 2004620160513104939730000s1973 coAa 0 0 EL d0 aadE86.99902059eE86.99902059fS41.27856855gS41.27856855zSite 3990 aadE133.71880822eE143.71880822fN8.13138602gN3.13138602zSite 4030 aadE171.49599516eE171.49599516fS56.3556026gS56.3556026zSite 1530 aadE119.63111094eE169.63111094fN11.62690851gN6.62690851zSite 1201 aValois, Arie10aTest Publication 2004600528naaa 2200133zu 4500001000800000005001500008008004100023034006800064034007200132034007200204034007400276100001700350245002700367 2004720160513104939730000s1973 coAa 0 0 EL d0 aadW23.6918351eW23.6918351fN5.50055924gN5.50055924zSite 4950 aadW71.72678006eW51.72678006fN80.68048573gN72.68048573zSite 4190 aadW72.47081023eW72.47081023fS13.50689659gS13.50689659zSite 5050 aadW159.51582506eW109.51582506fS12.01041499gS22.01041499zSite 3211 aGiard, Dodie10aTest Publication 2004700364naaa 2200109zu 4500001000800000005001500008008004100023034007200064034007200136100001900208245002700227 2004820160513104939730000s1973 coAa 0 0 EL d0 aadW98.39221287eW98.39221287fN20.00645186gN20.00645186zSite 1400 aadW35.22129012eW25.22129012fS11.27828226gS21.27828226zSite 1821 aDryer, Verlene10aTest Publication 2004800365naaa 2200109zu 4500001000800000005001500008008004100023034007400064034007400138100001600212245002700228 2004920160513104939730000s1973 coAa 0 0 EL d0 aadW126.87139449eW126.87139449fS18.74530167gS18.74530167zSite 4470 aadW159.94018465eW109.94018465fS57.54337933gS67.54337933zSite 4601 aKohn, Clora10aTest Publication 2004900366naaa 2200109zu 4500001000800000005001500008008004100023034007200064034007200136100002100208245002700229 2005020160513104939730000s1973 coAa 0 0 EL d0 aadW159.88627958eW109.88627958fS3.02307754gS8.02307754zSite 3560 aadE109.5707309eE119.5707309fS48.38262915gS78.38262915zSite 5401 aSutter, Katheryn10aTest Publication 2005000358naaa 2200109zu 4500001000800000005001500008008004100023034007000064034007000134100001700204245002700221 2005120160513104939730000s1973 coAa 0 0 EL d0 aadE19.87166027eE24.87166027fS35.8561532gS75.8561532zSite 3760 aadE71.3886803eE81.3886803fS25.17975703gS55.17975703zSite 1071 aWalberg, Pia10aTest Publication 2005100590naaa 2200157zu 4500001000800000005001500008008004100023034005400064034005400118034005400172034005400226034005400280034005400334100001700388245002700405 2005220160513104939730000s1973 coAa 0 0 EL d0 aadE0475840eE0475840fN315244gN315244zSite 3620 aadE0250120eE0250120fS051344gS051344zSite 4610 aadE0392838eE0392838fN123637gN123637zSite 3470 aadW0041819eW0041819fN670212gN670212zSite 3540 aadW0514641eW0514641fS325055gS325055zSite 5520 aadW0532607eW0432607fN401312gN201312zSite 2221 aHove, Armand10aTest Publication 2005200329naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100002000172245002700192 2005320160513104939730000s1973 coAa 0 0 EL d0 aadW0670504eW0670504fS394806gS394806zSite 3640 aadW0380213eW0280213fN411500gN211500zSite 1091 aWestray, Shizue10aTest Publication 2005300326naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001700172245002700189 2005420160513104939730000s1973 coAa 0 0 EL d0 aadW0254536eW0254536fS244501gS244501zSite 2570 aadW0934339eW0634339fN451934gN251934zSite 5111 aOros, Cierra10aTest Publication 2005400330naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100002100172245002700193 2005520160513104939730000s1973 coAa 0 0 EL d0 aadW1652138eW1652138fN004809gN004809zSite 3750 aadE0435513eE0535513fN510216gN310216zSite 2421 aBlacker, Minerva10aTest Publication 2005500329naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100002000172245002700192 2005620160513104939730000s1973 coAa 0 0 EL d0 aadW1043920eW1043920fS011057gS011057zSite 2450 aadW0234238eW0184238fN682608gN382608zSite 4221 aLaurich, Stuart10aTest Publication 2005600328naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001900172245002700191 2005720160513104939730000s1973 coAa 0 0 EL d0 aadW1722040eW1722040fS352841gS352841zSite 2820 aadW1665858eW1565858fN683913gN383913zSite 1871 aKarter, Marvis10aTest Publication 2005700264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2005820160513104939730000s1973 coAa 0 0 EL d0 aadW0981303eW0981303fN194221gN194221zSite 1021 aFavela, Lorretta10aTest Publication 2005800262naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001900118245002700137 2005920160513104939730000s1973 coAa 0 0 EL d0 aadW1612904eW1412904fS130045gS230045zSite 4881 aHultgren, Josh10aTest Publication 2005900327naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001800172245002700190 2006020160513104939730000s1973 coAa 0 0 EL d0 aadW0960543eW0960543fS052755gS052755zSite 5170 aadW1062423eW0762423fN181516gN081516zSite 1581 aMain, Neville10aTest Publication 2006000326naaa 2200109zu 4500001000800000005001500008008004100023034005400064034005400118100001700172245002700189 2006120160513104939730000s1973 coAa 0 0 EL d0 aadW1681205eW1681205fN335749gN335749zSite 1440 aadW1684935eW1384935fS551611gS751611zSite 2101 aShaw, Denice10aTest Publication 2006100278naaa 2200097zu 4500001000800000005001500008008004100023034007000064100001900134245002700153 2006220160513104939730000s1973 coAa 0 0 EL d0 aad+72.62860542e+72.62860542f-50.0640401g-50.0640401zSite 3861 aJasik, Micaela10aTest Publication 2006200280naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002100134245002700155 2006320160513104939730000s1973 coAa 0 0 EL d0 aad+79.53785174e+79.53785174f+6.56562911g+6.56562911zSite 1381 aRupp, Georgeanna10aTest Publication 2006300277naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001600136245002700152 2006420160513104939730000s1973 coAa 0 0 EL d0 aad+91.40655977e+91.40655977f-68.68188538g-68.68188538zSite 3761 aSears, Meri10aTest Publication 2006400280naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002100134245002700155 2006520160513104939730000s1973 coAa 0 0 EL d0 aad+90.5240303e+90.5240303f+16.31382075g+16.31382075zSite 2991 aWinrow, Sanjuana10aTest Publication 2006500279naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002000134245002700154 2006620160513104939730000s1973 coAa 0 0 EL d0 aad+51.37763177e+51.37763177f+0.65207263g+0.65207263zSite 4471 aSutter, Karolyn10aTest Publication 2006600280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2006720160513104939730000s1973 coAa 0 0 EL d0 aad+173.34512334e+173.34512334f+28.0049181g+28.0049181zSite 1541 aArechiga, Moon10aTest Publication 2006700277naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001600136245002700152 2006820160513104939730000s1973 coAa 0 0 EL d0 aad-15.99213608e-15.99213608f+13.65438911g+13.65438911zSite 4251 aSaar, Danna10aTest Publication 2006800280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2006920160513104939730000s1973 coAa 0 0 EL d0 aad+137.98717735e+137.98717735f-65.0442013g-65.0442013zSite 1001 aMirabito, Shin10aTest Publication 2006900278naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001700136245002700153 2007020160513104939730000s1973 coAa 0 0 EL d0 aad-117.4711533e-117.4711533f+38.23863124g+38.23863124zSite 3651 aWalberg, Pia10aTest Publication 2007000282naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002100136245002700157 2007120160513104939730000s1973 coAa 0 0 EL d0 aad+95.03142217e+95.03142217f-14.54137057g-14.54137057zSite 2361 aBalicki, Nicolas10aTest Publication 2007100276naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001500136245002700151 2007220160513104939730000s1973 coAa 0 0 EL d0 aad+34.55902657e+34.55902657f-27.96661882g-27.96661882zSite 5541 aOram, Donn10aTest Publication 2007200279naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001800136245002700154 2007320160513104939730000s1973 coAa 0 0 EL d0 aad+125.34051527e+125.34051527f+9.81883652g+9.81883652zSite 1211 aMurden, Claud10aTest Publication 2007300279naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001600138245002700154 2007420160513104939730000s1973 coAa 0 0 EL d0 aad+110.92377605e+110.92377605f+31.06593861g+31.06593861zSite 4421 aPerla, Jong10aTest Publication 2007400284naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002100138245002700159 2007520160513104939730000s1973 coAa 0 0 EL d0 aad+111.98924724e+111.98924724f+52.12174162g+52.12174162zSite 1501 aDobyns, Wilfredo10aTest Publication 2007500282naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002300134245002700157 2007620160513104939730000s1973 coAa 0 0 EL d0 aad+155.34616323e+155.34616323f-7.8061926g-7.8061926zSite 3231 aGrizzell, Griselda10aTest Publication 2007600282naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002100136245002700157 2007720160513104939730000s1973 coAa 0 0 EL d0 aad-45.37116744e-45.37116744f-45.10756474g-45.10756474zSite 1551 aSharrow, Sherron10aTest Publication 2007700279naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001800136245002700154 2007820160513104939730000s1973 coAa 0 0 EL d0 aad-125.8758505e-125.8758505f+37.66553345g+37.66553345zSite 4951 aOverall, Lula10aTest Publication 2007800280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2007920160513104939730000s1973 coAa 0 0 EL d0 aad-125.7131481e-125.7131481f-65.01937325g-65.01937325zSite 2721 aHersh, Sheldon10aTest Publication 2007900278naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001700136245002700153 2008020160513104939730000s1973 coAa 0 0 EL d0 aad-167.5160645e-167.5160645f+48.54645955g+48.54645955zSite 4591 aFouche, Hsiu10aTest Publication 2008000281naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001800138245002700156 2008120160513104939730000s1973 coAa 0 0 EL d0 aadE118.55522501eE118.55522501fS59.40043873gS59.40043873zSite 4161 aPerine, Alisa10aTest Publication 2008100280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2008220160513104939730000s1973 coAa 0 0 EL d0 aadE19.95358751eE19.95358751fN66.64677012gN66.64677012zSite 4031 aWalter, Jasper10aTest Publication 2008200285naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002400136245002700160 2008320160513104939730000s1973 coAa 0 0 EL d0 aadW61.22077604eW61.22077604fS26.01320081gS26.01320081zSite 3771 aGreenblatt, Sherill10aTest Publication 2008300283naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002200136245002700158 2008420160513104939730000s1973 coAa 0 0 EL d0 aadW38.52101948eW38.52101948fN42.22226461gN42.22226461zSite 5481 aCrafts, Claudette10aTest Publication 2008400284naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002100138245002700159 2008520160513104939730000s1973 coAa 0 0 EL d0 aadE132.70789703eE132.70789703fN38.04866994gN38.04866994zSite 1881 aCounter, Modesto10aTest Publication 2008500281naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002000136245002700156 2008620160513104939730000s1973 coAa 0 0 EL d0 aadW89.35020317eW89.35020317fN47.93266336gN47.93266336zSite 3091 aCrowder, Carlee10aTest Publication 2008600280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2008720160513104939730000s1973 coAa 0 0 EL d0 aadW125.32764205eW125.32764205fS4.19255958gS4.19255958zSite 3411 aJone, Federico10aTest Publication 2008700282naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002100136245002700157 2008820160513104939730000s1973 coAa 0 0 EL d0 aadW168.93025338eW168.93025338fS6.02872258gS6.02872258zSite 2641 aSchock, Lisandra10aTest Publication 2008800282naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002300134245002700157 2008920160513104939730000s1973 coAa 0 0 EL d0 aadW98.77347908eW98.77347908fS42.6798184gS42.6798184zSite 1191 aSpadaro, Geraldine10aTest Publication 2008900278naaa 2200097zu 4500001000800000005001500008008004100023034007000064100001900134245002700153 2009020160513104939730000s1973 coAa 0 0 EL d0 aadE85.8591623eE85.8591623fN66.00524174gN66.00524174zSite 3251 aSwanson, Mario10aTest Publication 2009000286naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002300138245002700161 2009120160513104939730000s1973 coAa 0 0 EL d0 aadE137.11729029eE137.11729029fN39.84518323gN39.84518323zSite 1601 aCornforth, Desiree10aTest Publication 2009100276naaa 2200097zu 4500001000800000005001500008008004100023034007000064100001700134245002700151 2009220160513104939730000s1973 coAa 0 0 EL d0 aadW65.3799254eW65.3799254fS46.40296668gS46.40296668zSite 3221 aLam, Cristen10aTest Publication 2009200283naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002000138245002700158 2009320160513104939730000s1973 coAa 0 0 EL d0 aadE176.53377147eE176.53377147fN22.77971971gN22.77971971zSite 3131 aFrankum, Thomas10aTest Publication 2009300284naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002300136245002700159 2009420160513104939730000s1973 coAa 0 0 EL d0 aadW27.45737117eW27.45737117fS55.67694086gS55.67694086zSite 3631 aMagallon, Christia10aTest Publication 2009400282naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001900138245002700157 2009520160513104939730000s1973 coAa 0 0 EL d0 aadE175.12324693eE175.12324693fS38.39888377gS38.39888377zSite 4401 aIverson, Beryl10aTest Publication 2009500274naaa 2200097zu 4500001000800000005001500008008004100023034007000064100001500134245002700149 2009620160513104939730000s1973 coAa 0 0 EL d0 aadW23.4142272eW23.4142272fN29.80880028gN29.80880028zSite 3131 aOrtiz, Tia10aTest Publication 2009600276naaa 2200097zu 4500001000800000005001500008008004100023034007000064100001700134245002700151 2009720160513104939730000s1973 coAa 0 0 EL d0 aadW18.33091801eW18.33091801fS6.31305738gS6.31305738zSite 3991 aBogard, Noma10aTest Publication 2009700281naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002000136245002700156 2009820160513104939730000s1973 coAa 0 0 EL d0 aadW72.31173246eW72.31173246fS46.75721273gS46.75721273zSite 3311 aWalston, Verlie10aTest Publication 2009800281naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002000136245002700156 2009920160513104939730000s1973 coAa 0 0 EL d0 aadW48.97356546eW48.97356546fN36.74731237gN36.74731237zSite 3291 aInnocent, Leisa10aTest Publication 2009900277naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001600136245002700152 2010020160513104939730000s1973 coAa 0 0 EL d0 aadW91.61595667eW91.61595667fN11.85462394gN11.85462394zSite 2661 aKohn, Clora10aTest Publication 2010000280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2010120160513104939730000s1973 coAa 0 0 EL d0 aadW99.81880793eW99.81880793fN56.31339558gN56.31339558zSite 1271 aLinney, Samual10aTest Publication 2010100283naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002000138245002700158 2010220160513104939730000s1973 coAa 0 0 EL d0 aadW125.07883932eW125.07883932fS45.00178881gS45.00178881zSite 2591 aMerrifield, Kym10aTest Publication 2010200262naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001900118245002700137 2010320160513104939730000s1973 coAa 0 0 EL d0 aadE0362614eE0362614fS011543gS011543zSite 1811 aBucher, Margit10aTest Publication 2010300264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2010420160513104939730000s1973 coAa 0 0 EL d0 aadE0181242eE0181242fS291354gS291354zSite 2271 aSteenberg, Shila10aTest Publication 2010400264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2010520160513104939730000s1973 coAa 0 0 EL d0 aadE0983727eE0983727fN682744gN682744zSite 5311 aBlacker, Minerva10aTest Publication 2010500261naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001800118245002700136 2010620160513104939730000s1973 coAa 0 0 EL d0 aadE0963319eE0963319fN151338gN151338zSite 1301 aHanley, Karla10aTest Publication 2010600264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2010720160513104939730000s1973 coAa 0 0 EL d0 aadW0753627eW0753627fN265359gN265359zSite 3661 aMackson, Yolanda10aTest Publication 2010700263naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002000118245002700138 2010820160513104939730000s1973 coAa 0 0 EL d0 aadE1792143eE1792143fS752257gS752257zSite 5231 aHarrill, Mariko10aTest Publication 2010800265naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002200118245002700140 2010920160513104939730000s1973 coAa 0 0 EL d0 aadW0953048eW0953048fS170735gS170735zSite 4021 aBehringer, Anitra10aTest Publication 2010900258naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001500118245002700133 2011020160513104939730000s1973 coAa 0 0 EL d0 aadW1184933eW1184933fN152720gN152720zSite 3511 aRowse, Ida10aTest Publication 2011000264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2011120160513104939730000s1973 coAa 0 0 EL d0 aadW1374144eW1374144fS081546gS081546zSite 1531 aSutter, Katheryn10aTest Publication 2011100263naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002000118245002700138 2011220160513104939730000s1973 coAa 0 0 EL d0 aadW1705954eW1705954fS082612gS082612zSite 3541 aBeckett, Tamala10aTest Publication 2011200259naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001600118245002700134 2011320160513104939730000s1973 coAa 0 0 EL d0 aadE0783553eE0783553fS255532gS255532zSite 4171 aBetty, Eryn10aTest Publication 2011300265naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002200118245002700140 2011420160513104939730000s1973 coAa 0 0 EL d0 aadE1284246eE1284246fS020216gS020216zSite 2121 aLetsinger, Tamiko10aTest Publication 2011400267naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002400118245002700142 2011520160513104939730000s1973 coAa 0 0 EL d0 aadE0521421eE0521421fS022222gS022222zSite 4701 aCantrell, Cornelius10aTest Publication 2011500260naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001700118245002700135 2011620160513104939730000s1973 coAa 0 0 EL d0 aadE0331639eE0331639fS283049gS283049zSite 4771 aLeyva, Marie10aTest Publication 2011600265naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002200118245002700140 2011720160513104939730000s1973 coAa 0 0 EL d0 aadE0760744eE0760744fN480042gN480042zSite 4401 aBeveridge, Albina10aTest Publication 2011700261naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001800118245002700136 2011820160513104939730000s1973 coAa 0 0 EL d0 aadW0712539eW0712539fN295730gN295730zSite 5271 aBramhall, Ria10aTest Publication 2011800260naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001700118245002700135 2011920160513104939730000s1973 coAa 0 0 EL d0 aadE1681553eE1681553fN524510gN524510zSite 1391 aHurt, Millie10aTest Publication 2011900260naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001700118245002700135 2012020160513104939730000s1973 coAa 0 0 EL d0 aadW0140130eW0140130fS010120gS010120zSite 4641 aWulff, Nilsa10aTest Publication 2012000263naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002000118245002700138 2012120160513104939730000s1973 coAa 0 0 EL d0 aadW0782218eW0782218fS753724gS753724zSite 5041 aFirth, Theressa10aTest Publication 2012100263naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002000118245002700138 2012220160513104939730000s1973 coAa 0 0 EL d0 aadW1330422eW1330422fN034324gN034324zSite 4511 aWorster, Marita10aTest Publication 2012200265naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002200118245002700140 2012320160513104939730000s1973 coAa 0 0 EL d0 aadW0843206eW0843206fS553424gS553424zSite 2961 aOlveda, Jenniffer10aTest Publication 2012300263naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002000118245002700138 2012420160513104939730000s1973 coAa 0 0 EL d0 aadW1582630eW1582630fS142429gS142429zSite 2791 aMessner, Leanna10aTest Publication 2012400275naaa 2200097zu 4500001000800000005001500008008004100023034006900064100001700133245002700150 2012520160513104939730000s1973 coAa 0 0 EL d0 aad-12.49085466e-7.49085466f+9.71187917g+4.71187917zSite 1731 aBevill, Sook10aTest Publication 2012500282naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002100136245002700157 2012620160513104939730000s1973 coAa 0 0 EL d0 aad+71.12605143e+81.12605143f+20.40214021g+10.40214021zSite 1211 aSchock, Lisandra10aTest Publication 2012600281naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001800138245002700156 2012720160513104939730000s1973 coAa 0 0 EL d0 aad+104.20929332e+154.20929332f+23.15670674g+13.15670674zSite 2831 aAbner, Shelli10aTest Publication 2012700282naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002100136245002700157 2012820160513104939730000s1973 coAa 0 0 EL d0 aad-130.3358613e-90.33586126f+54.37067475g+34.37067475zSite 3061 aNecessary, Dante10aTest Publication 2012800277naaa 2200097zu 4500001000800000005001500008008004100023034007300064100001500137245002700152 2012920160513104939730000s1973 coAa 0 0 EL d0 aad+89.51162859e+109.51162859f-22.88853952g-42.88853952zSite 1271 aAmyx, Lyle10aTest Publication 2012900281naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001800138245002700156 2013020160513104939730000s1973 coAa 0 0 EL d0 aad+120.94552421e+130.94552421f+80.21089126g+77.21089126zSite 5191 aGranda, Isiah10aTest Publication 2013000280naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002100134245002700155 2013120160513104939730000s1973 coAa 0 0 EL d0 aad+166.31983556e+176.31983556f-64.114475g-74.114475zSite 1291 aFavela, Lorretta10aTest Publication 2013100277naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001600136245002700152 2013220160513104939730000s1973 coAa 0 0 EL d0 aad-33.35817709e-23.35817709f-26.04958019g-56.04958019zSite 3441 aDaub, Lloyd10aTest Publication 2013200278naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001700136245002700153 2013320160513104939730000s1973 coAa 0 0 EL d0 aad+100.28119821e+150.28119821f-39.5929073g-79.5929073zSite 2811 aFines, Gregg10aTest Publication 2013300279naaa 2200097zu 4500001000800000005001500008008004100023034006800064100002200132245002700154 2013420160513104939730000s1973 coAa 0 0 EL d0 aad-98.724204e-38.72420401f+40.4924143g+20.4924143zSite 4941 aWalcott, Porfirio10aTest Publication 2013400280naaa 2200097zu 4500001000800000005001500008008004100023034007300064100001800137245002700155 2013520160513104939730000s1973 coAa 0 0 EL d0 aad-170.45022257e-156.4502226f+25.08138959g+15.08138959zSite 4151 aGranda, Isiah10aTest Publication 2013500281naaa 2200097zu 4500001000800000005001500008008004100023034007300064100001900137245002700156 2013620160513104939730000s1973 coAa 0 0 EL d0 aad+93.74890612e+143.74890612f-13.32263614g-23.32263614zSite 2561 aCutter, Annika10aTest Publication 2013600279naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001800136245002700154 2013720160513104939730000s1973 coAa 0 0 EL d0 aad-66.87358455e-46.87358455f+70.98897026g+50.98897026zSite 5431 aHanley, Karla10aTest Publication 2013700276naaa 2200097zu 4500001000800000005001500008008004100023034007000064100001700134245002700151 2013820160513104939730000s1973 coAa 0 0 EL d0 aad-172.9265117e-162.9265117f-1.47082029g-6.47082029zSite 1691 aDalke, Isiah10aTest Publication 2013800281naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002200134245002700156 2013920160513104939730000s1973 coAa 0 0 EL d0 aad+33.61230501e+43.61230501f-4.12575355g-9.12575355zSite 3771 aBehringer, Anitra10aTest Publication 2013900281naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002000136245002700156 2014020160513104939730000s1973 coAa 0 0 EL d0 aad+75.10477259e+95.10477259f+73.86871739g+53.86871739zSite 2831 aWilhoite, Kathi10aTest Publication 2014000282naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001900138245002700157 2014120160513104939730000s1973 coAa 0 0 EL d0 aad+125.92523664e+135.92523664f-15.86550515g-25.86550515zSite 1841 aJasik, Micaela10aTest Publication 2014100283naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002000138245002700158 2014220160513104939730000s1973 coAa 0 0 EL d0 aad+112.65337661e+122.65337661f+78.04277975g+68.04277975zSite 5271 aStrine, Tabitha10aTest Publication 2014200276naaa 2200097zu 4500001000800000005001500008008004100023034006900064100001800133245002700151 2014320160513104939730000s1973 coAa 0 0 EL d0 aad-10.83945484e-5.83945484f-30.6046605g-60.6046605zSite 1311 aOverall, Lula10aTest Publication 2014300279naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002000134245002700154 2014420160513104939730000s1973 coAa 0 0 EL d0 aad-15.53775937e-10.53775937f-46.3793589g-66.3793589zSite 2041 aMathisen, Dayle10aTest Publication 2014400281naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002000136245002700156 2014520160513104939730000s1973 coAa 0 0 EL d0 aad-147.1524264e-127.1524264f-70.97214091g-75.97214091zSite 4281 aWilhoite, Kathi10aTest Publication 2014500278naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001700136245002700153 2014620160513104939730000s1973 coAa 0 0 EL d0 aad+73.14760572e+93.14760572f-44.44305202g-64.44305202zSite 4881 aFines, Gregg10aTest Publication 2014600280naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001700138245002700155 2014720160513104939730000s1973 coAa 0 0 EL d0 aadE143.91680691eE153.91680691fN69.05928016gN59.05928016zSite 2301 aKinnan, Enda10aTest Publication 2014700281naaa 2200097zu 4500001000800000005001500008008004100023034007000064100002200134245002700156 2014820160513104939730000s1973 coAa 0 0 EL d0 aadE109.2953057eE159.2953057fN75.6021786gN72.6021786zSite 2351 aHartfield, Shelba10aTest Publication 2014800282naaa 2200097zu 4500001000800000005001500008008004100023034007300064100002000137245002700157 2014920160513104939730000s1973 coAa 0 0 EL d0 aadE144.81897837eE164.81897837fN18.62668817gN8.62668817zSite 2871 aSkeens, Cordell10aTest Publication 2014900278naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001700136245002700153 2015020160513104939730000s1973 coAa 0 0 EL d0 aadW115.49585203eW85.49585203fN19.56564384gN9.56564384zSite 4151 aHove, Armand10aTest Publication 2015000280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2015120160513104939730000s1973 coAa 0 0 EL d0 aadW32.63289449eW22.63289449fS55.38513226gS75.38513226zSite 1201 aHaugh, Winford10aTest Publication 2015100279naaa 2200097zu 4500001000800000005001500008008004100023034007300064100001700137245002700154 2015220160513104939730000s1973 coAa 0 0 EL d0 aadE87.77332672eE107.77332672fS54.25951534gS74.25951534zSite 3431 aValois, Arie10aTest Publication 2015200279naaa 2200097zu 4500001000800000005001500008008004100023034006800064100002200132245002700154 2015320160513104939730000s1973 coAa 0 0 EL d0 aadW32.8377449eW22.8377449fS3.09135927gS8.09135927zSite 4141 aMcclaran, Kathern10aTest Publication 2015300277naaa 2200097zu 4500001000800000005001500008008004100023034007000064100001800134245002700152 2015420160513104939730000s1973 coAa 0 0 EL d0 aadE34.4627201eE44.4627201fN23.51119899gN13.51119899zSite 3351 aHeinrich, See10aTest Publication 2015400282naaa 2200097zu 4500001000800000005001500008008004100023034007200064100002100136245002700157 2015520160513104939730000s1973 coAa 0 0 EL d0 aadE11.25085415eE16.25085415fN62.44255328gN42.44255328zSite 3801 aSteenberg, Shila10aTest Publication 2015500278naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001700136245002700153 2015620160513104939730000s1973 coAa 0 0 EL d0 aadE35.02387108eE45.02387108fN51.72426854gN31.72426854zSite 1231 aNiccum, Alex10aTest Publication 2015600284naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002100138245002700159 2015720160513104939730000s1973 coAa 0 0 EL d0 aadE115.38134036eE145.38134036fS21.96462606gS41.96462606zSite 3801 aRussom, Clorinda10aTest Publication 2015700278naaa 2200097zu 4500001000800000005001500008008004100023034006600064100002300130245002700153 2015820160513104939730000s1973 coAa 0 0 EL d0 aadW79.5685706eW59.5685706fS3.2186524gS8.2186524zSite 4461 aMontanye, Criselda10aTest Publication 2015800280naaa 2200097zu 4500001000800000005001500008008004100023034007200064100001900136245002700155 2015920160513104939730000s1973 coAa 0 0 EL d0 aadE78.87843097eE98.87843097fS24.09348392gS44.09348392zSite 3721 aGalasso, Tatum10aTest Publication 2015900283naaa 2200097zu 4500001000800000005001500008008004100023034007400064100002000138245002700158 2016020160513104939730000s1973 coAa 0 0 EL d0 aadW179.09659396eW119.09659396fS39.32213026gS79.32213026zSite 4631 aLiptak, Caitlin10aTest Publication 2016000282naaa 2200097zu 4500001000800000005001500008008004100023034007400064100001900138245002700157 2016120160513104939730000s1973 coAa 0 0 EL d0 aadW163.27020238eW113.27020238fS36.75458726gS76.75458726zSite 1621 aGaetano, Dedra10aTest Publication 2016100261naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001800118245002700136 2016220160513104939730000s1973 coAa 0 0 EL d0 aadE1114626eE1214626fN752639gN452639zSite 4161 aTorian, Belva10aTest Publication 2016200262naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001900118245002700137 2016320160513104939730000s1973 coAa 0 0 EL d0 aadE0285018eE0335018fN271418gN171418zSite 2191 aMirabito, Shin10aTest Publication 2016300266naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002300118245002700141 2016420160513104939730000s1973 coAa 0 0 EL d0 aadE0330550eE0430550fN471910gN271910zSite 1651 aSpadaro, Geraldine10aTest Publication 2016400265naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002200118245002700140 2016520160513104939730000s1973 coAa 0 0 EL d0 aadW0501321eW0401321fN053851gN003851zSite 4651 aHartfield, Shelba10aTest Publication 2016500259naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001600118245002700134 2016620160513104939730000s1973 coAa 0 0 EL d0 aadE1541338eE1641338fS053823gS103823zSite 2621 aDaub, Lloyd10aTest Publication 2016600262naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001900118245002700137 2016720160513104939730000s1973 coAa 0 0 EL d0 aadW0722141eW0522141fS223403gS423403zSite 5141 aCutter, Annika10aTest Publication 2016700264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2016820160513104939730000s1973 coAa 0 0 EL d0 aadW0053928eW0003928fS472308gS772308zSite 1971 aErickson, Joette10aTest Publication 2016800264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2016920160513104939730000s1973 coAa 0 0 EL d0 aadW0675444eW0475444fN571945gN271945zSite 2791 aSharrow, Sherron10aTest Publication 2016900261naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001800118245002700136 2017020160513104939730000s1973 coAa 0 0 EL d0 aadE1064421eE1564421fN402522gN202522zSite 1701 aVidrio, Sofia10aTest Publication 2017000261naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001800118245002700136 2017120160513104939730000s1973 coAa 0 0 EL d0 aadW1254938eW0854938fN511735gN311735zSite 5101 aHeinrich, See10aTest Publication 2017100261naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001800118245002700136 2017220160513104939730000s1973 coAa 0 0 EL d0 aadW1344729eW0944729fN051424gN001424zSite 5111 aBoyland, Bebe10aTest Publication 2017200263naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002000118245002700138 2017320160513104939730000s1973 coAa 0 0 EL d0 aadE0932835eE1432835fS175621gS375621zSite 2151 aStrine, Tabitha10aTest Publication 2017300262naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001900118245002700137 2017420160513104939730000s1973 coAa 0 0 EL d0 aadE1701912eE1751912fS073104gS123104zSite 1931 aBornstein, Dia10aTest Publication 2017400266naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002300118245002700141 2017520160513104939730000s1973 coAa 0 0 EL d0 aadW1362047eW0962047fS250039gS550039zSite 4111 aGrizzell, Griselda10aTest Publication 2017500264naaa 2200097zu 4500001000800000005001500008008004100023034005400064100002100118245002700139 2017620160513104939730000s1973 coAa 0 0 EL d0 aadW0545835eW0445835fS450010gS650010zSite 5091 aFalzone, Natasha10aTest Publication 2017600259naaa 2200097zu 4500001000800000005001500008008004100023034005400064100001600118245002700134 2017720160513104939730000s1973 coAa 0 0 EL d0 aadE1455446eE1555446fS373045gS773045zSite 3521 aJaco, Angle10aTest Publication 20177 \ No newline at end of file diff --git a/tests/data/geo.mrc.properties b/tests/data/geo.mrc.properties new file mode 100644 index 0000000000000000000000000000000000000000..f2276cb805563c6425819894751b12a90f02656f --- /dev/null +++ b/tests/data/geo.mrc.properties @@ -0,0 +1,6 @@ +id = 001, (pattern_map.id_prefix), first +pattern_map.id_prefix.pattern_0 = (.+)=>geo$1 +bbox_geo = custom, getAllCoordinates +long_lat = custom, getPointCoordinates +long_lat_display = custom, getDisplayCoordinates +long_lat_label = 034z diff --git a/themes/bootprint3/css/compiled.css b/themes/bootprint3/css/compiled.css index f244e191068455a5937b604525f01f69ac5827b3..71e105917ea0d205e71fe748184ef31e1052d6c5 100644 --- a/themes/bootprint3/css/compiled.css +++ b/themes/bootprint3/css/compiled.css @@ -5,4 +5,4 @@ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label,.result .format,.sidebar .format{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#12538B;text-decoration:none}a:hover,a:focus{color:#092b47;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:5px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:3px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:18px;margin-bottom:18px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:18px;margin-bottom:9px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:9px;margin-bottom:9px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:33px}h2,.h2{font-size:27px}h3,.h3{font-size:23px}h4,.h4{font-size:17px}h5,.h5{font-size:13px}h6,.h6{font-size:12px}p{margin:0 0 9px}.lead{margin-bottom:18px;font-size:14px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:19.5px}}small,.small{font-size:92%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#619144}a.text-primary:hover,a.text-primary:focus{color:#4a6e34}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff;background-color:#619144}a.bg-primary:hover,a.bg-primary:focus{background-color:#4a6e34}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:8px;margin:36px 0 18px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:9px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin:0;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:18px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:9px 18px;margin:0 0 18px;font-size:16.25px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:18px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:3px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:2px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;box-shadow:none}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:3px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:7px;padding-right:7px}@media (min-width:768px){.container{width:734px}}@media (min-width:992px){.container{width:952px}}@media (min-width:1200px){.container{width:952px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:7px;padding-right:7px}.row{margin-left:-7px;margin-right:-7px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:7px;padding-right:7px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:18px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:13.5px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:18px;font-size:19.5px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #777}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:4px;font-size:13px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:26px;padding:3px 5px;font-size:13px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#619144;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(97, 145, 68, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(97, 145, 68, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{border:0;background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:26px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:22px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:41px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:18px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:4px;padding-bottom:4px;margin-bottom:0;min-height:31px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:22px;padding:1px 2px;font-size:12px;line-height:1.5;border-radius:2px}select.input-sm{height:22px;line-height:22px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:22px;padding:1px 2px;font-size:12px;line-height:1.5;border-radius:2px}.form-group-sm select.form-control{height:22px;line-height:22px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:22px;min-height:30px;padding:2px 2px;font-size:12px;line-height:1.5}.input-lg{height:41px;padding:8px 5px;font-size:17px;line-height:1.3333333;border-radius:5px}select.input-lg{height:41px;line-height:41px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:41px;padding:8px 5px;font-size:17px;line-height:1.3333333;border-radius:5px}.form-group-lg select.form-control{height:41px;line-height:41px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:41px;min-height:35px;padding:9px 5px;font-size:17px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:32.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:26px;height:26px;line-height:26px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:41px;height:41px;line-height:41px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:22px;height:22px;line-height:22px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:23px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:4px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:22px}.form-horizontal .form-group{margin-left:-7px;margin-right:-7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:4px}}.form-horizontal .has-feedback .form-control-feedback{right:7px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:9px;font-size:17px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:2px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:3px 5px;font-size:13px;line-height:1.42857143;border-radius:3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .btn-default.dropdown-toggle{color:#fff;background-color:#333;border-color:#adadad}.btn-primary{color:#fff;background-color:#619144;border-color:#fff}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#4a6e34;border-color:#bfbfbf}.btn-primary:hover{color:#fff;background-color:#4a6e34;border-color:#e0e0e0}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#4a6e34;border-color:#e0e0e0}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#fff;background-color:#3a5628;border-color:#bfbfbf}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#619144;border-color:#fff}.btn-primary .badge{color:#619144;background-color:#fff}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .btn-primary.dropdown-toggle{color:#619144;background-color:#fff;border-color:#e0e0e0}.btn-success{color:#fff;background-color:#028302;border-color:#fff}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#015101;border-color:#bfbfbf}.btn-success:hover{color:#fff;background-color:#015101;border-color:#e0e0e0}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#015101;border-color:#e0e0e0}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#fff;background-color:#012e01;border-color:#bfbfbf}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#028302;border-color:#fff}.btn-success .badge{color:#028302;background-color:#fff}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .btn-success.dropdown-toggle{color:#028302;background-color:#fff;border-color:#e0e0e0}.btn-info{color:#fff;background-color:#1C5F74;border-color:#fff}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#123d4b;border-color:#bfbfbf}.btn-info:hover{color:#fff;background-color:#123d4b;border-color:#e0e0e0}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#123d4b;border-color:#e0e0e0}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#fff;background-color:#0b262e;border-color:#bfbfbf}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#1C5F74;border-color:#fff}.btn-info .badge{color:#1C5F74;background-color:#fff}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .btn-info.dropdown-toggle{color:#1C5F74;background-color:#fff;border-color:#e0e0e0}.btn-warning{color:#fff;background-color:#A56100;border-color:#fff}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#724300;border-color:#bfbfbf}.btn-warning:hover{color:#fff;background-color:#724300;border-color:#e0e0e0}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#724300;border-color:#e0e0e0}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#fff;background-color:#4e2e00;border-color:#bfbfbf}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#A56100;border-color:#fff}.btn-warning .badge{color:#A56100;background-color:#fff}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .btn-warning.dropdown-toggle{color:#A56100;background-color:#fff;border-color:#e0e0e0}.btn-danger{color:#fff;background-color:#A41915;border-color:#fff}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#77120f;border-color:#bfbfbf}.btn-danger:hover{color:#fff;background-color:#77120f;border-color:#e0e0e0}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#77120f;border-color:#e0e0e0}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#fff;background-color:#570d0b;border-color:#bfbfbf}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#A41915;border-color:#fff}.btn-danger .badge{color:#A41915;background-color:#fff}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .btn-danger.dropdown-toggle{color:#A41915;background-color:#fff;border-color:#e0e0e0}.btn-link{color:#12538B;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#092b47;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:8px 5px;font-size:17px;line-height:1.3333333;border-radius:5px}.btn-sm,.btn-group-sm>.btn{padding:1px 2px;font-size:12px;line-height:1.5;border-radius:2px}.btn-xs,.btn-group-xs>.btn{padding:1px 1px;font-size:12px;line-height:1.5;border-radius:2px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:13px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:3px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:8px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#619144}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:3px;border-top-left-radius:3px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:41px;padding:8px 5px;font-size:17px;line-height:1.3333333;border-radius:5px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:41px;line-height:41px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:22px;padding:1px 2px;font-size:12px;line-height:1.5;border-radius:2px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:22px;line-height:22px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:3px 5px;font-size:13px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:3px}.input-group-addon.input-sm{padding:1px 2px;font-size:12px;border-radius:2px}.input-group-addon.input-lg{padding:8px 5px;font-size:17px;border-radius:5px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:5px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#12538B}.nav .nav-divider{height:1px;margin:8px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:3px 3px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:3px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:3px 3px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:3px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#619144}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:3px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:3px 3px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:65px;margin-bottom:0;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:3px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:7px;padding-left:7px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-7px;margin-left:-7px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:23.5px 7px;font-size:17px;line-height:18px;height:65px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-7px}}.navbar-toggle{position:relative;float:right;margin-right:7px;padding:9px 10px;margin-top:15.5px;margin-bottom:15.5px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:3px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:11.75px -7px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:18px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:18px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:23.5px;padding-bottom:23.5px}}.navbar-form{margin-left:-7px;margin-right:-7px;padding:10px 7px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:19.5px;margin-bottom:19.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:3px;border-top-left-radius:3px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:19.5px;margin-bottom:19.5px}.navbar-btn.btn-sm{margin-top:21.5px;margin-bottom:21.5px}.navbar-btn.btn-xs{margin-top:21.5px;margin-bottom:21.5px}.navbar-text{margin-top:23.5px;margin-bottom:23.5px}@media (min-width:768px){.navbar-text{float:left;margin-left:7px;margin-right:7px}}@media (min-width:768px){.navbar-left{float:left !important;float:left}.navbar-right{float:right !important;float:right;margin-right:-7px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#132531;border-color:#0a1319}.navbar-default .navbar-brand{color:#fff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#068139;background-color:transparent}.navbar-default .navbar-text{color:#fff}.navbar-default .navbar-nav>li>a{color:#fff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#fff;background-color:#068139}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#0a1319}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#fff;color:#132531}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#fff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#fff;background-color:#068139}}.navbar-default .navbar-link{color:#fff}.navbar-default .navbar-link:hover{color:#132531}.navbar-default .btn-link{color:#fff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#132531}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#fff}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:6px 20px;margin-bottom:18px;list-style:none;background-color:#FFF;border-radius:3px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#777}.breadcrumb>.active{color:#333}.pagination{display:inline-block;padding-left:0;margin:18px 0;border-radius:3px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:3px 5px;line-height:1.42857143;text-decoration:none;color:#12538B;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#092b47;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#fff;background-color:#5bc0de;border-color:#5bc0de;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:8px 5px;font-size:17px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:5px;border-top-left-radius:5px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:5px;border-top-right-radius:5px}.pagination-sm>li>a,.pagination-sm>li>span{padding:1px 2px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:2px;border-top-left-radius:2px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:2px;border-top-right-radius:2px}.pager{padding-left:0;margin:18px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.label,.result .format,.sidebar .format{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#619144}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#4a6e34}.label-success{background-color:#028302}.label-success[href]:hover,.label-success[href]:focus{background-color:#015101}.label-info,.result .format,.sidebar .format{background-color:#1C5F74}.label-info[href]:hover,.label-info[href]:focus{background-color:#123d4b}.label-warning{background-color:#A56100}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#724300}.label-danger{background-color:#A41915}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#77120f}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#12538B;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:20px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:5px;padding-left:7px;padding-right:7px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:59px}}.thumbnail{display:block;padding:4px;margin-bottom:18px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:3px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#12538B}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:18px;border:1px solid transparent;border-radius:3px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:18px;margin-bottom:18px;background-color:#f5f5f5;border-radius:3px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:18px;color:#fff;text-align:center;background-color:#619144;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#028302}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#1C5F74}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#A56100}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#A41915}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item,.result.embedded .getFull.expanded,.result.embedded .loading{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{text-decoration:none;color:#555;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#619144;border-color:#619144}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#cce1c0}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:18px;background-color:#fff;border:1px solid transparent;border-radius:3px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:5px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:2px;border-top-left-radius:2px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:15px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:2px;border-top-left-radius:2px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:5px;padding-right:5px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:2px;border-top-left-radius:2px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:2px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:2px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:2px;border-bottom-right-radius:2px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:2px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:2px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:18px}.panel-group .panel{margin-bottom:0;border-radius:3px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#619144}.panel-primary>.panel-heading{color:#fff;background-color:#619144;border-color:#619144}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#619144}.panel-primary>.panel-heading .badge{color:#619144;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#619144}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:3px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:5px}.well-sm{padding:9px;border-radius:2px}.close,.group .group-close{float:right;font-size:19.5px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:5px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:3px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:13px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:13px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:4px 4px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;-moz-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}/*! * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../../bootstrap3/css/fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{width:1px;height:1px;margin:-1px;clip:rect(0, 0, 0, 0);clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0 0 0 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0 0 0 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0, rgba(0,0,0,0.5) 100%);box-shadow:0 0 0 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:.5em;left:.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder,input:-ms-input-placeholder,textarea:-ms-input-placeholder,input::-ms-input-placeholder,textarea::-ms-input-placeholder,input::placeholder,textarea::placeholder{color:#888}.sr-only{clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only:focus{background-color:#fff;border-radius:3px;clip:auto;color:#132531;display:block;font-size:13px;height:65px;line-height:18px;padding:23.5px 7px;position:absolute;left:5px;top:5px;text-decoration:none;text-transform:none;width:auto;z-index:100000}.navbar-brand{font-size:20px}.group{position:relative;background:#eee;border-radius:3px;border:1px solid #c8c8c8;margin-top:0;margin-bottom:.5em !important}.group .add_search_link{display:inline-block;margin-top:4px}.group .group-close{position:absolute;top:.3em;right:.5em;opacity:.4;z-index:2}.group .search{margin-bottom:2px}.group .search .close{opacity:.8}@media (min-width:768px){.group{padding:10px 10px 10px 25px}.group [class^=col-]{padding-left:0}}@media (max-width:767px){.group .search .middle{float:left;width:90%}.group .group-close{top:.5em;right:1em;opacity:.6}}@media (max-width:991px){.group .form-control{max-width:none}}#groupPlaceHolder{display:block;padding:6px}.template-dir-eds.template-name-advanced legend{margin-bottom:0}.template-dir-eds.template-name-advanced .no-js .group:nth-child(n+3){display:none}.template-dir-eds.template-name-advanced .search .close a{margin-left:-2em}.alphabrowse{border-collapse:separate}.alphabrowse .lcc{width:20%}.alphabrowse .titles{width:10%;text-align:center}.alphabrowse tr.browse-match td{border-top:.2em solid #619144;border-bottom:.2em solid #619144}.alphabrowse tr.browse-match td:first-child{border-left:.2em solid #619144}.alphabrowse tr.browse-match td:last-child{border-right:.2em solid #619144}.autocomplete-results{position:absolute;margin:0;margin-top:2px;padding:0;border:1px solid lightgray;background-color:#fff;border-radius:3px;overflow:hidden;z-index:50}.autocomplete-results .item{display:block;margin:0;padding:.75rem 1.25rem;border-bottom:1px solid lightgray;cursor:pointer}.autocomplete-results .item:last-child{border:0}.autocomplete-results .item:hover{background-color:#fff}.autocomplete-results .item.loading{background-color:#fff}.autocomplete-results .item.selected{background-color:#619144;color:#fff}.autocomplete-results .item small{display:block;color:darkgray}.fa-grid:before{content:"\f00a"}.fa-visual:before{content:"\f008"}.fa-x:before{content:"\f0f6"}.fa-atlas:before{content:"\f14e"}.fa-book:before{content:"\f02d"}.fa-braille:before{content:"\f0a6"}.fa-cdrom:before{content:"\f109"}.fa-chart:before{content:"\f012"}.fa-chipcartridge:before{content:"\f109"}.fa-collage:before{content:"\f03e"}.fa-disccartridge:before{content:"\f109"}.fa-drawing:before{content:"\f03e"}.fa-ebook:before{content:"\f0f6"}.fa-electronic:before{content:"\f1c6"}.fa-filmstrip:before{content:"\f008"}.fa-flashcard:before{content:"\f0e7"}.fa-floppydisk:before{content:"\f0c7"}.fa-globe:before{content:"\f0ac"}.fa-journal:before{content:"\f0f6"}.fa-kit:before{content:"\f0b1"}.fa-manuscript:before{content:"\f0f6"}.fa-map:before{content:"\f14e"}.fa-microfilm:before{content:"\f008"}.fa-motionpicture:before{content:"\f03d"}.fa-musicalscore:before{content:"\f001"}.fa-musicrecording:before{content:"\f001"}.fa-newspaper:before{content:"\f0f6"}.fa-online:before{content:"\f109"}.fa-painting:before{content:"\f03e"}.fa-photo:before{content:"\f03e"}.fa-photonegative:before{content:"\f03e"}.fa-physicalobject:before{content:"\f187"}.fa-print:before{content:"\f03e"}.fa-sensorimage:before{content:"\f03e"}.fa-serial:before{content:"\f0f6"}.fa-slide:before{content:"\f008"}.fa-software:before{content:"\f109"}.fa-soundcassette:before{content:"\f025"}.fa-sounddisc:before{content:"\f109"}.fa-soundrecording:before{content:"\f025"}.fa-tapecartridge:before{content:"\f109"}.fa-tapecassette:before{content:"\f025"}.fa-tapereel:before{content:"\f008"}.fa-transparency:before{content:"\f008"}.fa-unknown:before{content:"\f128"}.fa-video:before{content:"\f03d"}.fa-videocartridge:before{content:"\f03d"}.fa-videocassette:before{content:"\f03d"}.fa-videodisc:before{content:"\f109"}.fa-videoreel:before{content:"\f03d"}.hierarchy-tree .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-style:normal;font-weight:normal;cursor:pointer;text-decoration:inherit;speak:none}.hierarchy-tree .jstree-open>.jstree-ocl:before{content:"\f0d7"}.hierarchy-tree .jstree-closed>.jstree-ocl:before{content:"\f0da"}.hierarchy-tree .jstree-leaf>.jstree-ocl:before{content:" "}.hierarchy-tree .jstree-icon{width:16px;color:#000}.hierarchy-tree .jstree-anchor{padding:2px 5px;white-space:nowrap}.hierarchy-tree .jstree-container-ul,.hierarchy-tree .jstree-children{padding-left:16px}.hierarchy-tree .jstree-initial-node{display:none}.hierarchy-tree .jstree-clicked{color:#fff;background-color:#619144}.hierarchy-tree .jstree-clicked .jstree-icon{color:#fff}.hierarchy-tree .jstree-search a{font-style:italic;color:#8b0000;font-weight:bold}#hierarchyTreeHolder{overflow-x:hidden;border-right:1px solid #eee}#hierarchyTree .currentHierarchy>a,#hierarchyTree .currentRecord a{font-weight:bold;color:#000}.facet .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-weight:normal;font-style:normal;text-decoration:inherit;cursor:pointer;speak:none}.facet .jstree-default .jstree-open>.jstree-ocl:before{content:"\f0d7"}.facet .jstree-default .jstree-closed>.jstree-ocl:before{content:"\f0da"}.facet .jstree-default .jstree-leaf>.jstree-ocl:before{content:" "}.jstree-facet li span.main{display:block;padding-left:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jstree-facet .jstree-container-ul{padding:0}.jstree-facet .jstree-container-ul>li.active,.jstree-facet .jstree-container-ul>li.active a.jstree-anchor{background-color:#265680;color:#fff}li.jstree-facet,li.jstree-node{list-style:none}li.jstree-facet .badge{cursor:text}li.jstree-facet ul{padding-left:20px}.lightbox-only{display:none}#modal .lightbox-only{display:initial}#modal{background-color:rgba(0,0,0,0.2)}#modal .modal-content>.close{position:absolute;right:-50px;top:0;z-index:2;font-size:32pt;color:#fff;opacity:.7}#modal .modal-content>.close:hover{opacity:1}#modal .modal-body h1,#modal .modal-body h2{margin-top:.3rem;margin-bottom:1.3rem}#modal .cart-controls .btn{margin-bottom:4px}#modal .cart-controls .checkbox{padding-bottom:1em}#modal .cart-controls~hr{margin-top:0}.lightbox-scroll{overflow-y:auto}.offcanvas-overlay,.offcanvas-toggle{display:none}@media screen and (max-width:767px){body.offcanvas{overflow-x:hidden}body.offcanvas .sidebar{position:fixed;height:100%;top:0;width:75%;padding-left:0;padding-right:0;overflow-y:auto}body.offcanvas .sidebar h4{padding-left:5px}body.offcanvas .sidebar .checkbox{margin-left:25px}body.offcanvas .sidebar .list-group,body.offcanvas .sidebar .list-group-item{border-left:0;border-right:0;border-radius:0 !important}body.offcanvas.active{overflow-y:hidden}body.offcanvas.offcanvas-left{padding-left:23px}body.offcanvas.offcanvas-left .main{background:#FFF}body.offcanvas.offcanvas-left.active{margin-left:75%;margin-right:-75%}body.offcanvas.offcanvas-left.active .sidebar{left:0}body.offcanvas.offcanvas-left.active .offcanvas-overlay{right:-75%}body.offcanvas.offcanvas-left.active .offcanvas-toggle{left:75%}body.offcanvas.offcanvas-left .sidebar{left:-75%}body.offcanvas.offcanvas-left .offcanvas-overlay{right:-100%}body.offcanvas.offcanvas-left .offcanvas-toggle{border-radius:0 2px 2px 0;left:0}body.offcanvas.offcanvas-right{padding-right:23px}body.offcanvas.offcanvas-right .main>.container{background:#FFF}body.offcanvas.offcanvas-right.active{margin-left:-75%;margin-right:75%}body.offcanvas.offcanvas-right.active .sidebar{right:0}body.offcanvas.offcanvas-right.active .offcanvas-overlay{left:-75%}body.offcanvas.offcanvas-right.active .offcanvas-toggle{right:75%}body.offcanvas.offcanvas-right .sidebar{right:-75%}body.offcanvas.offcanvas-right .offcanvas-overlay{left:-100%}body.offcanvas.offcanvas-right .offcanvas-toggle{border-radius:2px 0 0 2px;right:0}body.offcanvas .offcanvas-overlay{display:block;position:fixed;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.3);z-index:3}body.offcanvas .offcanvas-toggle{display:block;position:fixed;top:50%;width:calc(25px);padding:20px 0;background:#619144;color:#EEE;text-align:center;z-index:5}body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle,body.offcanvas .offcanvas-toggle *{cursor:pointer}body.offcanvas,body.offcanvas .sidebar,body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle{-webkit-transition:all .25s ease-out;-o-transition:all .25s ease-out;transition:all .25s ease-out}}.citation .pace-car th,.citation .pace-car td{border:0;padding:0}.citation th{text-align:right}.item-notes ul{padding-left:2rem}.recordcover{max-height:300px}.tagList .tag{display:inline-block;margin:0 1px 1px;border-radius:4px;padding:3px 3px;font-size:13px;line-height:1.42857143;border-radius:3px}.tagList .tag.selected{background-color:#619144}.tagList .tag.selected a{color:#fff}.tagList .tag.selected .badge{color:#222;background-color:#fff}.tagList .tag.selected .badge:hover{color:#a94442}.tagList .tag .badge .fa{width:12px}.tagList button{border:0}.tagList .tag-form{display:inline}.tagList.loggedin .tag:not(.selected) .badge:hover{background-color:#028302}.subject-line:hover{color:#999}.subject-line:hover a{color:#092b47}.subject-line a:hover~a{color:#999;text-decoration:none}.record .format::after{content:", "}.result .record .format::after,.record .format:last-child::after{content:""}.marc-row-LEADER,.marc-row-006,.marc-row-007,.marc-row-008{white-space:pre-wrap}.bulkActionButtons label{display:inline-block}.bulkActionButtons label input{margin-top:2px}@media (max-width:767px){.grid{min-height:250px}}.result{padding-top:1.5rem}.result .checkbox{float:left;padding-right:.5rem}.result .media,.result .media-body{overflow:visible}.result .media-body .row{margin-left:0;margin-right:0}.result .media{margin-top:0;margin-bottom:1rem}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media-left,.result .media-right{padding:0;text-align:center}.result .media-left a,.result .media-right a{display:inline-block;text-align:center}.result .media-left img,.result .media-right img{max-width:none;max-height:300px}.result .media-left.small a,.result .media-right.small a,.result .media-left.small>img,.result .media-right.small>img{width:60px}.result .media-left.small a img,.result .media-right.small a img,.result .media-left.small>img img,.result .media-right.small>img img{max-width:60px}.result .media-left.medium a,.result .media-right.medium a,.result .media-left.medium>img,.result .media-right.medium>img{width:100px}.result .media-left.medium a img,.result .media-right.medium a img,.result .media-left.medium>img img,.result .media-right.medium>img img{max-width:100px}.result .media-left.large a,.result .media-right.large a,.result .media-left.large>img,.result .media-right.large>img{width:160px}.result .media-left.large a img,.result .media-right.large a img,.result .media-left.large>img img,.result .media-right.large>img img{max-width:160px}.result .media-left{padding-left:10px}.result .media-right{padding-right:10px}.result .title{font-weight:bold}.result .list-tab-content.record .img-col{display:none}.result .list-tab-content.record .info-col{width:100%}@media (max-width:767px){.result a{text-decoration:underline}}@media (max-width:530px){.result .checkbox{padding:0}.result .media-left{padding-right:0}}.result.embedded .getFull{display:block;margin-left:1.5rem;padding-right:30px;border-left:1px solid transparent}.result.embedded .getFull.expanded{margin-top:-11px;margin-left:.75rem;padding-left:.75rem;border-top-left-radius:3px;border-top-right-radius:3px}.result.embedded .getFull.expanded::before{content:'\25BC';position:absolute;right:15px;color:#555}.result.embedded .loading{margin-left:.75rem;padding:1rem;background:#fff}.result.embedded .long-view{margin-left:.75rem;padding:.5rem;border:1px solid #ddd;background-color:#fff;border-bottom-left-radius:3px;border-bottom-right-radius:3px}.result.embedded .long-view .tab-content{padding:0}.result.embedded .list-tabs{margin-bottom:0}.result.embedded .list-tab-toggle{cursor:pointer}.result.embedded .list-tab-content{padding:1rem}.search-controls .alert{margin-bottom:0}.searchtools a{padding:0 .5em}.title-in-heading{font-size:inherit;font-style:italic}.wikipedia img{margin-right:1rem}.narrow-toggle{text-align:center}.sidebar label:not(.list-group-item){margin-left:20px}.sidebar .list-group.facet .list-group-item.title{cursor:pointer}.sidebar .list-group.facet .list-group-item.title.collapsed{border-radius:3px}.sidebar .list-group.facet .list-group-item.title.collapsed:after{content:'\25BC'}.sidebar .list-group.facet .list-group-item.title:after{content:'\25B2';float:right}.sidebar .collapse .list-group-item,.sidebar .collapsing .list-group-item{border-top-left-radius:0;border-top-right-radius:0}.sidebar .collapse .list-group-item[id^=more],.sidebar .collapsing .list-group-item[id^=more]{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.sidebar #side-collapse-publishDate .list-group-item{border-bottom-left-radius:3px;border-bottom-right-radius:3px}label.list-group-item{margin-top:0;padding-left:35px;font-weight:normal;border-radius:0}.list-group-item.title{font-weight:bold}.sidebar .facet a{text-decoration:none}.top-row .applied{font-weight:bold}.top-row .applied:hover{color:#a94442}.top-row .applied:hover .fa.fa-check:before{content:"\f00d"}.full-facet-list{margin-top:1rem}#similar-items-carousel .carousel-indicators{bottom:0}#similar-items-carousel .carousel-indicators li{width:8px;height:8px;margin:2px;background-color:rgba(255,255,255,0.3);border-color:#222}#similar-items-carousel .hover-overlay{position:relative;display:block;min-width:150px;min-height:200px;margin:auto;text-align:center}#similar-items-carousel .hover-overlay img{max-width:100%;margin:10px 0}#similar-items-carousel .hover-overlay .content{position:absolute;top:0;left:0;display:none;width:100%;height:100%;padding:.5em .5em 0;color:#fff;background-color:rgba(0,0,0,0.5)}#similar-items-carousel .hover-overlay:hover .content{display:block}#similar-items-carousel .item{padding:0 4em}.slider-container{padding:4px 10px;text-align:center}.slider-container .slider.slider-horizontal{width:100%}.slider-container .slider-track{background:#777;box-shadow:inset 0 1px 0 rgba(0,0,0,0.4)}.slider-container .slider-handle{background:#619144;background-image:none;border:1px solid #619144;box-shadow:none;opacity:.9}.slider-container .slider-handle:hover,.slider-container .slider-handle:active,.slider-container .slider-handle:focus{opacity:1;background:#FFF;border-color:#777}.slider-container .slider-handle:active,.slider-container .slider-handle:focus{border-color:#619144}.slider-container .slider-selection{background:#CCC;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.3)}.slider-container input{display:none}.alert.alert-info a{text-decoration:underline}.btn.disabled:active,.btn.disabled:focus,.btn.disabled:hover{color:#000}header .dropdown form{display:none}.list-unstyled{margin:0}.highlight,mark{background:#ff6;padding:.1em .2em}.icon-bar{background-color:#888}img{max-width:100%}.popover{width:250px}.sub-breadcrumb{padding:5px 10px;white-space:nowrap}.sub-breadcrumb li{display:inline-block}.sub-breadcrumb li+li:before{padding-left:5px;padding-right:5px;color:#777;content:"/\00a0"}.tab-content{padding:4px}@media (max-width:991px){header .container.navbar{margin-bottom:0}.searchForm{margin-top:0}}@media (min-width:768px){h2{font-size:23px;font-weight:normal}h3{font-size:20px;font-weight:normal}.form-control{max-width:400px}}@media (max-width:767px){h2{font-size:20px}h3{font-size:16px}.searchForm{padding-top:0}}.has-error{margin-bottom:0}.sms-error{margin-bottom:0}.sms-error .help-block,.sms-error .control-label,.sms-error .radio,.sms-error .checkbox,.sms-error .radio-inline,.sms-error .checkbox-inline,.sms-error.radio label,.sms-error.checkbox label,.sms-error.radio-inline label,.sms-error.checkbox-inline label{color:#a94442}.sms-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.sms-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.sms-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.sms-error .form-control-feedback{color:#a94442}.help-block.with-errors{margin:0;padding-top:3px;padding-bottom:3px}.help-block.with-errors:empty{padding:0}.badge a{color:#fff}.browse.list-group .list-group-item{word-wrap:break-word}.browse.list-group .list-group-item.view-record{padding:2px 4px;font-size:85%;text-align:right;border-top:0}.cart-controls .checkbox{line-height:2.5em;padding-right:1em}#dateVisColorSettings{background-color:#fff;fill:#eaeaea;outline-color:#c38835;stroke:#619144}.table{table-layout:fixed;word-wrap:break-word}.node{position:absolute;box-sizing:content-box;margin:-1px;overflow:hidden;font:10px sans-serif;line-height:12px;border:1px solid white}.node div{margin-top:0}.toplevel{border:2px solid black}.node .label{position:absolute;bottom:0;left:0;min-height:1px;padding:2px 4px;font-size:85%;background-color:rgba(0,0,0,0.5);border-radius:0;text-shadow:none}.notalabel{color:#000}#viz-instructions{padding-top:600px}span[class^="services-"],span[class*=" services-"] span::before{content:", "}span[class^="services-"],span[class*=" services-"] span:first-of-type::before{content:""}.bp-icon,.fa-x,i.fa-archive,i.fa-asterisk,i.fa-atlas,i.fa-bell,i.fa-book,i.fa-bookbag-add,i.fa-bookbag-delete,i.fa-bookbag-empty,i.fa-bookmark,i.fa-braille,i.fa-cancel-all-holds,i.fa-cancel-all-storage-retrieval-requests,i.fa-cancel-holds,i.fa-cancel-storage-retrieval-requests,i.fa-cdrom,i.fa-chart,i.fa-chipcartridge,i.fa-collage,i.fa-close,i.fa-disccartridge,i.fa-drawing,i.fa-ebook,i.fa-edit,i.fa-electronic,i.fa-email,i.fa-envelope,i.fa-envelope-o,i.fa-exchange,i.fa-external-link,i.fa-filmstrip,i.fa-flag,i.fa-flashcard,i.fa-floppydisk,i.fa-globe,i.fa-grid,i.fa-heart,i.fa-home,i.fa-inbox,i.fa-journal,i.fa-kit,i.fa-leaf,.fa-sitemap,i.fa-list,i.fa-list-alt,i.fa-export,i.fa-lock,i.fa-manuscript,i.fa-map,i.fa-microfilm,i.fa-minus-circle,i.fa-minus-sign,i.fa-mobile,i.fa-motionpicture,i.fa-musicalscore,i.fa-musicrecording,i.fa-newspaper,i.fa-ok,i.fa-online,i.fa-painting,i.fa-photo,i.fa-photonegative,i.fa-physicalobject,i.fa-plus,i.fa-plus-circle,i.fa-print,i.fa-qrcode,i.fa-remove,i.fa-renew,i.fa-renew-all,i.fa-report,i.fa-rss,i.fa-save,i.fa-search,i.fa-sensorimage,i.fa-serial,i.fa-shopping-cart,i.fa-sign-in,i.fa-sign-out,i.fa-slide,i.fa-software,i.fa-soundcassette,i.fa-sounddisc,i.fa-soundrecording,i.fa-spinner,i.fa-star,i.fa-status-unknown,i.fa-suitcase,i.fa-tapecartridge,i.fa-tapecassette,i.fa-tapereel,i.fa-transparency,i.fa-trash,i.fa-trash-o,i.fa-tree,i.fa-tree-muted,i.fa-unknown,i.fa-usd,i.fa-user,i.fa-video,i.fa-videocartridge,i.fa-videocassette,i.fa-videodisc,i.fa-videoreel,i.fa-visual,#cart-empty-label i.fa-close{background-position:center center;background-repeat:no-repeat;color:transparent;content:'';display:inline-block;height:16px;margin:0;padding:0;text-shadow:none;vertical-align:text-bottom;width:16px}.fa-x{background-image:url('../images/icons/page_white.png')}i.fa-archive{background-image:url('../images/icons/package.png')}i.fa-asterisk{background-image:url('../images/icons/list.png')}i.fa-atlas{background-image:url('../images/icons/map.png')}i.fa-bell{background-image:url('../images/icons/bell.png')}i.fa-book{background-image:url('../images/icons/book.png')}i.fa-bookbag-add{background-image:url('../images/icons/bookbag_add.png')}i.fa-bookbag-delete{background-image:url('../images/icons/bookbag_delete.png')}i.fa-bookbag-empty{background-image:url('../images/icons/bookbag_empty.png')}i.fa-bookmark{background-image:url('../images/icons/bookmark_add.png')}i.fa-braille{background-image:url('../images/icons/page_red.png')}i.fa-cancel-all-holds{background-image:url('../images/icons/holdCancelAll.png')}i.fa-cancel-all-storage-retrieval-requests{background-image:url('../images/icons/holdCancelAll.png')}i.fa-cancel-holds{background-image:url('../images/icons/holdCancel.png')}i.fa-cancel-storage-retrieval-requests{background-image:url('../images/icons/holdCancel.png')}i.fa-cdrom{background-image:url('../images/icons/cd.png')}i.fa-chart{background-image:url('../images/icons/chart_bar.png')}i.fa-chipcartridge{background-image:url('../images/icons/server.png')}i.fa-collage{background-image:url('../images/icons/pictures.png')}i.fa-close{background-image:url('../images/icons/cross.png')}i.fa-disccartridge{background-image:url('../images/icons/cd.png')}i.fa-drawing{background-image:url('../images/icons/photo.png')}i.fa-ebook{background-image:url('../images/icons/book_addresses.png')}i.fa-edit{background-image:url('../images/icons/edit.png')}i.fa-electronic{background-image:url('../images/icons/mouse.png')}i.fa-email,i.fa-envelope,i.fa-envelope-o{background-image:url('../images/icons/email.png')}i.fa-exchange{background-image:url('../images/icons/arrow_refresh.png')}i.fa-external-link{background-image:url('../images/icons/link_go.png')}i.fa-filmstrip{background-image:url('../images/icons/film.png')}i.fa-flag{background-image:url('../images/icons/flag_red.png')}i.fa-flashcard{background-image:url('../images/icons/table_lightening.png')}i.fa-floppydisk{background-image:url('../images/icons/disk.png')}i.fa-globe{background-image:url('../images/icons/world.png')}i.fa-grid{background-image:url('../images/icons/view_grid.png')}i.fa-heart{background-image:url('../images/icons/heart.png')}i.fa-home{background-image:url('../images/icons/house.png')}i.fa-inbox{background-image:url('../images/icons/box.png')}i.fa-journal{background-image:url('../images/icons/book.png')}i.fa-kit{background-image:url('../images/icons/briefcase.png')}i.fa-leaf,.fa-sitemap{background-image:url('../images/icons/treeCurrent.png')}i.fa-list{background-image:url('../images/icons/view_list.png')}i.fa-list-alt,i.fa-export{background-image:url('../images/icons/application_add.png')}i.fa-lock{background-image:url('../images/icons/lock.png')}i.fa-manuscript{background-image:url('../images/icons/script.png')}i.fa-map{background-image:url('../images/icons/map.png')}i.fa-microfilm{background-image:url('../images/icons/film.png')}i.fa-minus-circle,i.fa-minus-sign{background-image:url('../images/icons/delete.png')}i.fa-mobile{background-image:url('../images/icons/phone.png')}i.fa-motionpicture{background-image:url('../images/icons/television.png')}i.fa-musicalscore{background-image:url('../images/icons/music.png')}i.fa-musicrecording{background-image:url('../images/icons/music.png')}i.fa-newspaper{background-image:url('../images/icons/newspaper.png')}i.fa-ok{background-image:url('../images/icons/tick.png')}i.fa-online{background-image:url('../images/icons/computer.png')}i.fa-painting{background-image:url('../images/icons/paintbrush.png')}i.fa-photo{background-image:url('../images/icons/photo.png')}i.fa-photonegative{background-image:url('../images/icons/film.png')}i.fa-physicalobject{background-image:url('../images/icons/box.png')}i.fa-plus{background-image:url('../images/icons/add.png')}i.fa-plus-circle{background-image:url('../images/icons/add.png')}i.fa-print{background-image:url('../images/icons/printer.png')}i.fa-qrcode{background-image:url('../images/icons/qrcode.png')}i.fa-remove{background-image:url('../images/icons/delete.png')}i.fa-renew{background-image:url('../images/icons/renew.png')}i.fa-renew-all{background-image:url('../images/icons/renewAll.png')}i.fa-report{background-image:url('../images/icons/report.png')}i.fa-rss{background-image:url('../images/icons/feed.png')}i.fa-save{background-image:url('../images/icons/disk.png')}i.fa-search{background-image:url('../images/icons/magnifier.png')}i.fa-sensorimage{background-image:url('../images/icons/photo.png')}i.fa-serial{background-image:url('../images/icons/page_white_stack.png')}i.fa-shopping-cart{background-image:url('../images/icons/cart.png')}i.fa-sign-in{background-image:url('../images/icons/door_in.png')}i.fa-sign-out{background-image:url('../images/icons/door_out.png')}i.fa-slide{background-image:url('../images/icons/film.png')}i.fa-software{background-image:url('../images/icons/drive_cd.png')}i.fa-soundcassette{background-image:url('../images/icons/sound.png')}i.fa-sounddisc{background-image:url('../images/icons/cd.png')}i.fa-soundrecording{background-image:url('../images/icons/sound.png')}i.fa-spinner{background-image:url('../images/icons/ajax_loading.gif')}i.fa-star{background-image:url('../images/icons/star.png')}i.fa-status-unknown{background-image:url('../images/icons/bullet_orange.png')}i.fa-suitcase{background-image:url('../images/icons/bookbag.png')}i.fa-tapecartridge{background-image:url('../images/icons/drive.png')}i.fa-tapecassette{background-image:url('../images/icons/drive.png')}i.fa-tapereel{background-image:url('../images/icons/film.png')}i.fa-transparency{background-image:url('../images/icons/film.png')}i.fa-trash,i.fa-trash-o{background-image:url('../images/icons/bin.png')}i.fa-tree{background-image:url('../images/icons/treeCurrent.png')}i.fa-tree-muted{background-image:url('../images/icons/treeMuted.png')}i.fa-unknown{background-image:url('../images/icons/page_white.png')}i.fa-usd{background-image:url('../images/icons/money_dollar.png')}i.fa-user{background-image:url('../images/icons/user.png')}i.fa-video{background-image:url('../images/icons/television.png')}i.fa-videocartridge{background-image:url('../images/icons/television.png')}i.fa-videocassette{background-image:url('../images/icons/television.png')}i.fa-videodisc{background-image:url('../images/icons/cd.png')}i.fa-videoreel{background-image:url('../images/icons/film.png')}i.fa-visual{background-image:url('../images/icons/view_visual.png')}body.rtl i.fa-external-link{background-image:url('../images/icons/link_go_rtl.png')}body.rtl i.fa-flag{background-image:url('../images/icons/flag_red_rtl.png')}#cart-empty-label i.fa-close{background-image:url('../images/icons/briefcase.png')}.searchHomeContent{float:none;margin:1em auto;width:90%}#advSearchForm .search{margin:0}.group .match{margin-top:.5em}.searchForm_lookfor,.searchForm_type{border-color:#619144}[name=searchForm]{margin:6px 8px 8px;padding:0}[name=searchForm] .clear-btn,[name=searchForm] .btn-primary,[name=searchForm] .form-control{font-size:14px;height:32px;padding:5px 8px}[name=searchForm] .clear-btn,[name=searchForm] .btn-primary[multiple],[name=searchForm] .form-control[multiple]{height:auto}@media (min-width:768px){[name=searchForm] .search-query{width:400px}}[name=searchForm] .nav-tabs{border-bottom:0;padding:0 6px}[name=searchForm] .nav-tabs li a{margin-bottom:-1px;border-bottom:0;padding-bottom:6px}[name=searchForm] .nav-tabs li a:hover{background:0 0;border-color:transparent;text-decoration:underline}[name=searchForm] .nav-tabs li.active a,[name=searchForm] .nav-tabs li.active a:hover{background:#FFF;border-color:#619144;border-bottom:0;text-decoration:none;z-index:5}body{background:#619144;font-size:13px}.container{background:#FFF;padding:0}.main .container{padding:0 4px 18px}a,.btn-link{color:#06C}a:hover,.btn-link:hover{color:#09F}.alert{padding:8px}.btn{padding:3px 5px 2px}.btn.btn-default{background:#eee;background-image:linear-gradient(#FFF, #ddd);border:1px solid #555;color:#222;text-shadow:0 1px 0 #FFF}.btn:not(.btn-default){font-weight:bold}.btn-danger,.btn-danger:hover{border-color:#6e110e}.btn-info,.btn-info:hover{border-color:#103743}.btn-primary,.btn-primary:hover{border-color:#456730}.btn-success,.btn-success:hover{border-color:#014701}.btn-warning,.btn-warning:hover{border-color:#683d00}#commentList .comment:nth-child(even){background:#eee}#dateVisColorSettings{stroke:#619144}#hierarchyRecord{background:#FFF}h2{margin:0 8px 8px}input[type=radio],input[type=checkbox]{margin:2px auto 0;padding:0 2px}.nav>li>a{padding:5px 10px}.nav-pills{display:table;margin:0 auto}.navbar{min-height:1px}.navbar-form{margin-top:5px;margin-bottom:5px}.pagination{display:table;margin:18px auto}.pagination>li>a,.pagination>li>span{padding:4px 12px 3px}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{background:#619144;border-color:#619144}.panel-heading{padding:0}.panel-heading a{cursor:pointer;display:inline-block;padding:6px;width:100%}.row:not(.top-row){padding:6px 4px;margin:0 -4px}.row>p{padding:0 1em}.sub-breadcrumb{padding:0 5px}.tab-content{padding:6px 8px;border:1px solid #eee;border-top:0}@media (max-width:767px){body{padding:6px}header{margin-top:0}.label,.result .format,.sidebar .format{font-size:85%}}@media (min-width:768px){.badge{font-size:85%;margin-top:1px}.label,.result .format,.sidebar .format{padding-top:.3em}.modal-dialog{width:650px}}header{margin-top:18px}header .fa.fa-bars{font-size:21px}header .navbar{border-radius:5px 5px 0 0;padding:0 10px}header .navbar .searchForm{display:none !important}header .navbar .navbar-brand{height:65px;width:170px;margin-top:5px;color:transparent;background-image:url('../images/vufind_logo.png');background-position:center center;background-repeat:no-repeat;background-size:contain}header .navbar .navbar-brand:active,header .navbar .navbar-brand:focus,header .navbar .navbar-brand:hover{color:transparent}header .navbar .navbar-brand.lang-ar{background-image:url('../images/vufind_logo_ar.png')}header .navbar .navbar-nav>li>a{padding:12px 6px}header .navbar .navbar-right{margin-top:12px}@media (max-width:767px){header .navbar .navbar-nav>li>a{padding:8px 24px}header .navbar .navbar-right{margin:0}}header .searchbox{background:linear-gradient(to bottom, #FFF, #EEE);display:block !important}header .searchbox .tab-content{border:0}header .searchbox .tab-content .navbar-text{margin:5px 10px 5px 0}@media (max-width:767px){header #header-collapse .navbar-right li{text-align:right}header .searchForm_type{margin-top:2px;margin-bottom:2px}}header .breadcrumb{border:1px solid #CCC;border-radius:0;border-width:1px 0;font-size:12px;margin-bottom:2px;padding:7px 20px 5px}footer{margin-bottom:36px}footer .container{border-radius:0 0 5px 5px;border-top:1px solid #ddd;padding-top:18px}footer hr{display:none}footer p{margin:0}footer ul{padding-left:30px}[id^=list].list-group .col-sm-9{margin:0}body.offcanvas .offcanvas-toggle{padding-bottom:18px;font-size:16px;background:#fff;box-shadow:0 0 2px #000;color:#619144}body.offcanvas .sidebar .list-group{color:#000}body.offcanvas.active .sidebar{color:#FFF}body.offcanvas.active .offcanvas-toggle{box-shadow:none}ul.random{list-style:none;padding:0;margin:0;text-align:justify}ul.random li{padding-bottom:10px}ul.random li img{margin:0 auto 1em}ul.random.image,ul.random.mixed{text-align:center}ul.random.image li img{margin:0 auto}#custom_recaptcha_widget{display:table}#custom_recaptcha_widget embed{display:none}#custom_recaptcha_widget #recaptcha_image{border:1px solid #000;padding:6px;margin:1em 0}#custom_recaptcha_widget #recaptcha_response_field{margin:0 .5em}#custom_recaptcha_widget>div>a{display:inline-block;float:left;margin:5px 10px 5px 0}.tagList button{margin-top:0;padding-top:0;padding-bottom:4px;font-size:95%;vertical-align:initial}.tagList button .fa-close{margin-top:3px}.bulkActionButtons{margin-bottom:6px}.result{padding:1rem;margin-left:-1.1rem}.result:nth-child(even){background-color:#eee}.result.embedded .getFull.expanded{margin-top:-6px;padding-top:.5rem;padding-bottom:.5rem}.result>p{padding:0 1em}.result .label{display:inline-block;margin-bottom:4px}.result .long-view .tab-content{background:#FFF}.result .media{margin:0}.result .row{padding:0}.result .savedLists{margin:0 0 4px;padding:4px 0 4px 6px}.result .savedLists ul{padding-left:18px}.search-controls label{text-align:left}@media (max-width:767px){.result .search-controls .form-inline{text-align:left}.search-controls{margin:4px -4px;padding:4px 0}}.sidebar .list-group{margin-bottom:5px}.sidebar .list-group label.list-group-item{padding-left:26px}.sidebar .list-group label.list-group-item input[type=checkbox]{margin-top:2px}.sidebar .list-group-item{padding:7px 10px 6px}.sidebar .list-group-item.active{color:#fff}.sidebar .list-group-item.active .badge{color:#E70}.sidebar .list-group-item.active,.sidebar .list-group-item.active:hover{background:#E70;border-color:#E70}.sidebar .list-group-item .badge a{color:#fff}.sidebar .slider-container{margin:4px auto 10px;width:95%}.sidebar .slider-container .slider-handle{background:#619144;opacity:1}.top-row .badge a{color:#fff}.top-row .badge a:hover{color:#A41915} \ No newline at end of file + */@font-face{font-family:'FontAwesome';src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../../bootstrap3/css/fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{width:1px;height:1px;margin:-1px;clip:rect(0, 0, 0, 0);clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0 0 0 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0 0 0 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0, rgba(0,0,0,0.5) 100%);box-shadow:0 0 0 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:.5em;left:.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder,input:-ms-input-placeholder,textarea:-ms-input-placeholder,input::-ms-input-placeholder,textarea::-ms-input-placeholder,input::placeholder,textarea::placeholder{color:#888}.sr-only{clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only:focus{background-color:#fff;border-radius:3px;clip:auto;color:#132531;display:block;font-size:13px;height:65px;line-height:18px;padding:23.5px 7px;position:absolute;left:5px;top:5px;text-decoration:none;text-transform:none;width:auto;z-index:100000}.navbar-brand{font-size:20px}.group{position:relative;background:#eee;border-radius:3px;border:1px solid #c8c8c8;margin-top:0;margin-bottom:.5em !important}.group .add_search_link{display:inline-block;margin-top:4px}.group .group-close{position:absolute;top:.3em;right:.5em;opacity:.4;z-index:2}.group .search{margin-bottom:2px}.group .search .close{opacity:.8}@media (min-width:768px){.group{padding:10px 10px 10px 25px}.group [class^=col-]{padding-left:0}}@media (max-width:767px){.group .search .middle{float:left;width:90%}.group .group-close{top:.5em;right:1em;opacity:.6}}@media (max-width:991px){.group .form-control{max-width:none}}#groupPlaceHolder{display:block;padding:6px}.template-dir-eds.template-name-advanced legend{margin-bottom:0}.template-dir-eds.template-name-advanced .no-js .group:nth-child(n+3){display:none}.template-dir-eds.template-name-advanced .search .close a{margin-left:-2em}.alphabrowse{border-collapse:separate}.alphabrowse .lcc{width:20%}.alphabrowse .titles{width:10%;text-align:center}.alphabrowse tr.browse-match td{border-top:.2em solid #619144;border-bottom:.2em solid #619144}.alphabrowse tr.browse-match td:first-child{border-left:.2em solid #619144}.alphabrowse tr.browse-match td:last-child{border-right:.2em solid #619144}.autocomplete-results{position:absolute;margin:0;margin-top:2px;padding:0;border:1px solid lightgray;background-color:#fff;border-radius:3px;overflow:hidden;z-index:50}.autocomplete-results .item{display:block;margin:0;padding:.75rem 1.25rem;border-bottom:1px solid lightgray;cursor:pointer}.autocomplete-results .item:last-child{border:0}.autocomplete-results .item:hover{background-color:#fff}.autocomplete-results .item.loading{background-color:#fff}.autocomplete-results .item.selected{background-color:#619144;color:#fff}.autocomplete-results .item small{display:block;color:darkgray}.fa-grid:before{content:"\f00a"}.fa-visual:before{content:"\f008"}.fa-x:before{content:"\f0f6"}.fa-atlas:before{content:"\f14e"}.fa-book:before{content:"\f02d"}.fa-braille:before{content:"\f0a6"}.fa-cdrom:before{content:"\f109"}.fa-chart:before{content:"\f012"}.fa-chipcartridge:before{content:"\f109"}.fa-collage:before{content:"\f03e"}.fa-disccartridge:before{content:"\f109"}.fa-drawing:before{content:"\f03e"}.fa-ebook:before{content:"\f0f6"}.fa-electronic:before{content:"\f1c6"}.fa-filmstrip:before{content:"\f008"}.fa-flashcard:before{content:"\f0e7"}.fa-floppydisk:before{content:"\f0c7"}.fa-globe:before{content:"\f0ac"}.fa-journal:before{content:"\f0f6"}.fa-kit:before{content:"\f0b1"}.fa-manuscript:before{content:"\f0f6"}.fa-map:before{content:"\f14e"}.fa-microfilm:before{content:"\f008"}.fa-motionpicture:before{content:"\f03d"}.fa-musicalscore:before{content:"\f001"}.fa-musicrecording:before{content:"\f001"}.fa-newspaper:before{content:"\f0f6"}.fa-online:before{content:"\f109"}.fa-painting:before{content:"\f03e"}.fa-photo:before{content:"\f03e"}.fa-photonegative:before{content:"\f03e"}.fa-physicalobject:before{content:"\f187"}.fa-print:before{content:"\f03e"}.fa-sensorimage:before{content:"\f03e"}.fa-serial:before{content:"\f0f6"}.fa-slide:before{content:"\f008"}.fa-software:before{content:"\f109"}.fa-soundcassette:before{content:"\f025"}.fa-sounddisc:before{content:"\f109"}.fa-soundrecording:before{content:"\f025"}.fa-tapecartridge:before{content:"\f109"}.fa-tapecassette:before{content:"\f025"}.fa-tapereel:before{content:"\f008"}.fa-transparency:before{content:"\f008"}.fa-unknown:before{content:"\f128"}.fa-video:before{content:"\f03d"}.fa-videocartridge:before{content:"\f03d"}.fa-videocassette:before{content:"\f03d"}.fa-videodisc:before{content:"\f109"}.fa-videoreel:before{content:"\f03d"}.hierarchy-tree .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-style:normal;font-weight:normal;cursor:pointer;text-decoration:inherit;speak:none}.hierarchy-tree .jstree-open>.jstree-ocl:before{content:"\f0d7"}.hierarchy-tree .jstree-closed>.jstree-ocl:before{content:"\f0da"}.hierarchy-tree .jstree-leaf>.jstree-ocl:before{content:" "}.hierarchy-tree .jstree-icon{width:16px;color:#000}.hierarchy-tree .jstree-anchor{padding:2px 5px;white-space:nowrap}.hierarchy-tree .jstree-container-ul,.hierarchy-tree .jstree-children{padding-left:16px}.hierarchy-tree .jstree-initial-node{display:none}.hierarchy-tree .jstree-clicked{color:#fff;background-color:#619144}.hierarchy-tree .jstree-clicked .jstree-icon{color:#fff}.hierarchy-tree .jstree-search a{font-style:italic;color:#8b0000;font-weight:bold}#hierarchyTreeHolder{overflow-x:hidden;border-right:1px solid #eee}#hierarchyTree .currentHierarchy>a,#hierarchyTree .currentRecord a{font-weight:bold;color:#000}.facet .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-weight:normal;font-style:normal;text-decoration:inherit;cursor:pointer;speak:none}.facet .jstree-default .jstree-open>.jstree-ocl:before{content:"\f0d7"}.facet .jstree-default .jstree-closed>.jstree-ocl:before{content:"\f0da"}.facet .jstree-default .jstree-leaf>.jstree-ocl:before{content:" "}.jstree-facet li span.main{display:block;padding-left:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jstree-facet .jstree-container-ul{padding:0}.jstree-facet .jstree-container-ul>li.active,.jstree-facet .jstree-container-ul>li.active a.jstree-anchor{background-color:#265680;color:#fff}li.jstree-facet,li.jstree-node{list-style:none}li.jstree-facet .badge{cursor:text}li.jstree-facet ul{padding-left:20px}.lightbox-only{display:none}#modal .lightbox-only{display:initial}#modal{background-color:rgba(0,0,0,0.2)}#modal .modal-content>.close{position:absolute;right:-50px;top:0;z-index:2;font-size:32pt;color:#fff;opacity:.7}#modal .modal-content>.close:hover{opacity:1}#modal .modal-body h1,#modal .modal-body h2{margin-top:.3rem;margin-bottom:1.3rem}#modal .cart-controls .btn{margin-bottom:4px}#modal .cart-controls .checkbox{padding-bottom:1em}#modal .cart-controls~hr{margin-top:0}.lightbox-scroll{overflow-y:auto}.offcanvas-overlay,.offcanvas-toggle{display:none}@media screen and (max-width:767px){body.offcanvas{overflow-x:hidden}body.offcanvas .sidebar{position:fixed;height:100%;top:0;width:75%;padding-left:0;padding-right:0;overflow-y:auto}body.offcanvas .sidebar h4{padding-left:5px}body.offcanvas .sidebar .checkbox{margin-left:25px}body.offcanvas .sidebar .list-group,body.offcanvas .sidebar .list-group-item{border-left:0;border-right:0;border-radius:0 !important}body.offcanvas.active{overflow-y:hidden}body.offcanvas.offcanvas-left{padding-left:23px}body.offcanvas.offcanvas-left .main{background:#FFF}body.offcanvas.offcanvas-left.active{margin-left:75%;margin-right:-75%}body.offcanvas.offcanvas-left.active .sidebar{left:0}body.offcanvas.offcanvas-left.active .offcanvas-overlay{right:-75%}body.offcanvas.offcanvas-left.active .offcanvas-toggle{left:75%}body.offcanvas.offcanvas-left .sidebar{left:-75%}body.offcanvas.offcanvas-left .offcanvas-overlay{right:-100%}body.offcanvas.offcanvas-left .offcanvas-toggle{border-radius:0 2px 2px 0;left:0}body.offcanvas.offcanvas-right{padding-right:23px}body.offcanvas.offcanvas-right .main>.container{background:#FFF}body.offcanvas.offcanvas-right.active{margin-left:-75%;margin-right:75%}body.offcanvas.offcanvas-right.active .sidebar{right:0}body.offcanvas.offcanvas-right.active .offcanvas-overlay{left:-75%}body.offcanvas.offcanvas-right.active .offcanvas-toggle{right:75%}body.offcanvas.offcanvas-right .sidebar{right:-75%}body.offcanvas.offcanvas-right .offcanvas-overlay{left:-100%}body.offcanvas.offcanvas-right .offcanvas-toggle{border-radius:2px 0 0 2px;right:0}body.offcanvas .offcanvas-overlay{display:block;position:fixed;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.3);z-index:3}body.offcanvas .offcanvas-toggle{display:block;position:fixed;top:50%;width:calc(25px);padding:20px 0;background:#619144;color:#EEE;text-align:center;z-index:5}body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle,body.offcanvas .offcanvas-toggle *{cursor:pointer}body.offcanvas,body.offcanvas .sidebar,body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle{-webkit-transition:all .25s ease-out;-o-transition:all .25s ease-out;transition:all .25s ease-out}}.citation .pace-car th,.citation .pace-car td{border:0;padding:0}.citation th{text-align:right}.item-notes ul{padding-left:2rem}.recordcover{max-height:300px}.tagList .tag{display:inline-block;margin:0 1px 1px;border-radius:4px;padding:3px 3px;font-size:13px;line-height:1.42857143;border-radius:3px}.tagList .tag.selected{background-color:#619144}.tagList .tag.selected a{color:#fff}.tagList .tag.selected .badge{color:#222;background-color:#fff}.tagList .tag.selected .badge:hover{color:#a94442}.tagList .tag .badge .fa{width:12px}.tagList button{border:0}.tagList .tag-form{display:inline}.tagList.loggedin .tag:not(.selected) .badge:hover{background-color:#028302}.subject-line:hover{color:#999}.subject-line:hover a{color:#092b47}.subject-line a:hover~a{color:#999;text-decoration:none}.record .format::after{content:", "}.result .record .format::after,.record .format:last-child::after{content:""}.marc-row-LEADER,.marc-row-006,.marc-row-007,.marc-row-008{white-space:pre-wrap}.bulkActionButtons label{display:inline-block}.bulkActionButtons label input{margin-top:2px}@media (max-width:767px){.grid{min-height:250px}}.result{padding-top:1.5rem}.result .checkbox{float:left;padding-right:.5rem}.result .media,.result .media-body{overflow:visible}.result .media-body .row{margin-left:0;margin-right:0}.result .media{margin-top:0;margin-bottom:1rem}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media-left,.result .media-right{padding:0;text-align:center}.result .media-left a,.result .media-right a{display:inline-block;text-align:center}.result .media-left img,.result .media-right img{max-width:none;max-height:300px}.result .media-left.small a,.result .media-right.small a,.result .media-left.small>img,.result .media-right.small>img{width:60px}.result .media-left.small a img,.result .media-right.small a img,.result .media-left.small>img img,.result .media-right.small>img img{max-width:60px}.result .media-left.medium a,.result .media-right.medium a,.result .media-left.medium>img,.result .media-right.medium>img{width:100px}.result .media-left.medium a img,.result .media-right.medium a img,.result .media-left.medium>img img,.result .media-right.medium>img img{max-width:100px}.result .media-left.large a,.result .media-right.large a,.result .media-left.large>img,.result .media-right.large>img{width:160px}.result .media-left.large a img,.result .media-right.large a img,.result .media-left.large>img img,.result .media-right.large>img img{max-width:160px}.result .media-left{padding-left:10px}.result .media-right{padding-right:10px}.result .title{font-weight:bold}.result .list-tab-content.record .img-col{display:none}.result .list-tab-content.record .info-col{width:100%}@media (max-width:767px){.result a{text-decoration:underline}}@media (max-width:530px){.result .checkbox{padding:0}.result .media-left{padding-right:0}}.result.embedded .getFull{display:block;margin-left:1.5rem;padding-right:30px;border-left:1px solid transparent}.result.embedded .getFull.expanded{margin-top:-11px;margin-left:.75rem;padding-left:.75rem;border-top-left-radius:3px;border-top-right-radius:3px}.result.embedded .getFull.expanded::before{content:'\25BC';position:absolute;right:15px;color:#555}.result.embedded .loading{margin-left:.75rem;padding:1rem;background:#fff}.result.embedded .long-view{margin-left:.75rem;padding:.5rem;border:1px solid #ddd;background-color:#fff;border-bottom-left-radius:3px;border-bottom-right-radius:3px}.result.embedded .long-view .tab-content{padding:0}.result.embedded .list-tabs{margin-bottom:0}.result.embedded .list-tab-toggle{cursor:pointer}.result.embedded .list-tab-content{padding:1rem}.search-controls .alert{margin-bottom:0}.searchtools a{padding:0 .5em}.title-in-heading{font-size:inherit;font-style:italic}.wikipedia img{margin-right:1rem}.geoItem{font-size:.9em;margin:0 0 10px}.narrow-toggle{text-align:center}.sidebar label:not(.list-group-item){margin-left:20px}.sidebar .list-group.facet .list-group-item.title{cursor:pointer}.sidebar .list-group.facet .list-group-item.title.collapsed{border-radius:3px}.sidebar .list-group.facet .list-group-item.title.collapsed:after{content:'\25BC'}.sidebar .list-group.facet .list-group-item.title:after{content:'\25B2';float:right}.sidebar .collapse .list-group-item,.sidebar .collapsing .list-group-item{border-top-left-radius:0;border-top-right-radius:0}.sidebar .collapse .list-group-item[id^=more],.sidebar .collapsing .list-group-item[id^=more]{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.sidebar #side-collapse-publishDate .list-group-item{border-bottom-left-radius:3px;border-bottom-right-radius:3px}label.list-group-item{margin-top:0;padding-left:35px;font-weight:normal;border-radius:0}.list-group-item.title{font-weight:bold}.sidebar .facet a{text-decoration:none}.top-row .applied{font-weight:bold}.top-row .applied:hover{color:#a94442}.top-row .applied:hover .fa.fa-check:before{content:"\f00d"}.full-facet-list{margin-top:1rem}#similar-items-carousel .carousel-indicators{bottom:0}#similar-items-carousel .carousel-indicators li{width:8px;height:8px;margin:2px;background-color:rgba(255,255,255,0.3);border-color:#222}#similar-items-carousel .hover-overlay{position:relative;display:block;min-width:150px;min-height:200px;margin:auto;text-align:center}#similar-items-carousel .hover-overlay img{max-width:100%;margin:10px 0}#similar-items-carousel .hover-overlay .content{position:absolute;top:0;left:0;display:none;width:100%;height:100%;padding:.5em .5em 0;color:#fff;background-color:rgba(0,0,0,0.5)}#similar-items-carousel .hover-overlay:hover .content{display:block}#similar-items-carousel .item{padding:0 4em}.slider-container{padding:4px 10px;text-align:center}.slider-container .slider.slider-horizontal{width:100%}.slider-container .slider-track{background:#777;box-shadow:inset 0 1px 0 rgba(0,0,0,0.4)}.slider-container .slider-handle{background:#619144;background-image:none;border:1px solid #619144;box-shadow:none;opacity:.9}.slider-container .slider-handle:hover,.slider-container .slider-handle:active,.slider-container .slider-handle:focus{opacity:1;background:#FFF;border-color:#777}.slider-container .slider-handle:active,.slider-container .slider-handle:focus{border-color:#619144}.slider-container .slider-selection{background:#CCC;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.3)}.slider-container input{display:none}.alert.alert-info a{text-decoration:underline}.btn.disabled:active,.btn.disabled:focus,.btn.disabled:hover{color:#000}header .dropdown form{display:none}.list-unstyled{margin:0}.highlight,mark{background:#ff6;padding:.1em .2em}.icon-bar{background-color:#888}img{max-width:100%}.popover{width:250px}.sub-breadcrumb{padding:5px 10px;white-space:nowrap}.sub-breadcrumb li{display:inline-block}.sub-breadcrumb li+li:before{padding-left:5px;padding-right:5px;color:#777;content:"/\00a0"}.tab-content{padding:4px}@media (max-width:991px){header .container.navbar{margin-bottom:0}.searchForm{margin-top:0}}@media (min-width:768px){h2{font-size:23px;font-weight:normal}h3{font-size:20px;font-weight:normal}.form-control{max-width:400px}}@media (max-width:767px){h2{font-size:20px}h3{font-size:16px}.searchForm{padding-top:0}}.has-error{margin-bottom:0}.sms-error{margin-bottom:0}.sms-error .help-block,.sms-error .control-label,.sms-error .radio,.sms-error .checkbox,.sms-error .radio-inline,.sms-error .checkbox-inline,.sms-error.radio label,.sms-error.checkbox label,.sms-error.radio-inline label,.sms-error.checkbox-inline label{color:#a94442}.sms-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.sms-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.sms-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.sms-error .form-control-feedback{color:#a94442}.help-block.with-errors{margin:0;padding-top:3px;padding-bottom:3px}.help-block.with-errors:empty{padding:0}.badge a{color:#fff}.browse.list-group .list-group-item{word-wrap:break-word}.browse.list-group .list-group-item.view-record{padding:2px 4px;font-size:85%;text-align:right;border-top:0}.cart-controls .checkbox{line-height:2.5em;padding-right:1em}#dateVisColorSettings{background-color:#fff;fill:#eaeaea;outline-color:#c38835;stroke:#619144}.table{table-layout:fixed;word-wrap:break-word}.node{position:absolute;box-sizing:content-box;margin:-1px;overflow:hidden;font:10px sans-serif;line-height:12px;border:1px solid white}.node div{margin-top:0}.toplevel{border:2px solid black}.node .label{position:absolute;bottom:0;left:0;min-height:1px;padding:2px 4px;font-size:85%;background-color:rgba(0,0,0,0.5);border-radius:0;text-shadow:none}.notalabel{color:#000}#viz-instructions{padding-top:600px}span[class^="services-"],span[class*=" services-"] span::before{content:", "}span[class^="services-"],span[class*=" services-"] span:first-of-type::before{content:""}.bp-icon,.fa-x,i.fa-archive,i.fa-asterisk,i.fa-atlas,i.fa-bell,i.fa-book,i.fa-bookbag-add,i.fa-bookbag-delete,i.fa-bookbag-empty,i.fa-bookmark,i.fa-braille,i.fa-cancel-all-holds,i.fa-cancel-all-storage-retrieval-requests,i.fa-cancel-holds,i.fa-cancel-storage-retrieval-requests,i.fa-cdrom,i.fa-chart,i.fa-chipcartridge,i.fa-collage,i.fa-close,i.fa-disccartridge,i.fa-drawing,i.fa-ebook,i.fa-edit,i.fa-electronic,i.fa-email,i.fa-envelope,i.fa-envelope-o,i.fa-exchange,i.fa-external-link,i.fa-filmstrip,i.fa-flag,i.fa-flashcard,i.fa-floppydisk,i.fa-globe,i.fa-grid,i.fa-heart,i.fa-home,i.fa-inbox,i.fa-journal,i.fa-kit,i.fa-leaf,.fa-sitemap,i.fa-list,i.fa-list-alt,i.fa-export,i.fa-lock,i.fa-manuscript,i.fa-map,i.fa-microfilm,i.fa-minus-circle,i.fa-minus-sign,i.fa-mobile,i.fa-motionpicture,i.fa-musicalscore,i.fa-musicrecording,i.fa-newspaper,i.fa-ok,i.fa-online,i.fa-painting,i.fa-photo,i.fa-photonegative,i.fa-physicalobject,i.fa-plus,i.fa-plus-circle,i.fa-print,i.fa-qrcode,i.fa-remove,i.fa-renew,i.fa-renew-all,i.fa-report,i.fa-rss,i.fa-save,i.fa-search,i.fa-sensorimage,i.fa-serial,i.fa-shopping-cart,i.fa-sign-in,i.fa-sign-out,i.fa-slide,i.fa-software,i.fa-soundcassette,i.fa-sounddisc,i.fa-soundrecording,i.fa-spinner,i.fa-star,i.fa-status-unknown,i.fa-suitcase,i.fa-tapecartridge,i.fa-tapecassette,i.fa-tapereel,i.fa-transparency,i.fa-trash,i.fa-trash-o,i.fa-tree,i.fa-tree-muted,i.fa-unknown,i.fa-usd,i.fa-user,i.fa-video,i.fa-videocartridge,i.fa-videocassette,i.fa-videodisc,i.fa-videoreel,i.fa-visual,#cart-empty-label i.fa-close{background-position:center center;background-repeat:no-repeat;color:transparent;content:'';display:inline-block;height:16px;margin:0;padding:0;text-shadow:none;vertical-align:text-bottom;width:16px}.fa-x{background-image:url('../images/icons/page_white.png')}i.fa-archive{background-image:url('../images/icons/package.png')}i.fa-asterisk{background-image:url('../images/icons/list.png')}i.fa-atlas{background-image:url('../images/icons/map.png')}i.fa-bell{background-image:url('../images/icons/bell.png')}i.fa-book{background-image:url('../images/icons/book.png')}i.fa-bookbag-add{background-image:url('../images/icons/bookbag_add.png')}i.fa-bookbag-delete{background-image:url('../images/icons/bookbag_delete.png')}i.fa-bookbag-empty{background-image:url('../images/icons/bookbag_empty.png')}i.fa-bookmark{background-image:url('../images/icons/bookmark_add.png')}i.fa-braille{background-image:url('../images/icons/page_red.png')}i.fa-cancel-all-holds{background-image:url('../images/icons/holdCancelAll.png')}i.fa-cancel-all-storage-retrieval-requests{background-image:url('../images/icons/holdCancelAll.png')}i.fa-cancel-holds{background-image:url('../images/icons/holdCancel.png')}i.fa-cancel-storage-retrieval-requests{background-image:url('../images/icons/holdCancel.png')}i.fa-cdrom{background-image:url('../images/icons/cd.png')}i.fa-chart{background-image:url('../images/icons/chart_bar.png')}i.fa-chipcartridge{background-image:url('../images/icons/server.png')}i.fa-collage{background-image:url('../images/icons/pictures.png')}i.fa-close{background-image:url('../images/icons/cross.png')}i.fa-disccartridge{background-image:url('../images/icons/cd.png')}i.fa-drawing{background-image:url('../images/icons/photo.png')}i.fa-ebook{background-image:url('../images/icons/book_addresses.png')}i.fa-edit{background-image:url('../images/icons/edit.png')}i.fa-electronic{background-image:url('../images/icons/mouse.png')}i.fa-email,i.fa-envelope,i.fa-envelope-o{background-image:url('../images/icons/email.png')}i.fa-exchange{background-image:url('../images/icons/arrow_refresh.png')}i.fa-external-link{background-image:url('../images/icons/link_go.png')}i.fa-filmstrip{background-image:url('../images/icons/film.png')}i.fa-flag{background-image:url('../images/icons/flag_red.png')}i.fa-flashcard{background-image:url('../images/icons/table_lightening.png')}i.fa-floppydisk{background-image:url('../images/icons/disk.png')}i.fa-globe{background-image:url('../images/icons/world.png')}i.fa-grid{background-image:url('../images/icons/view_grid.png')}i.fa-heart{background-image:url('../images/icons/heart.png')}i.fa-home{background-image:url('../images/icons/house.png')}i.fa-inbox{background-image:url('../images/icons/box.png')}i.fa-journal{background-image:url('../images/icons/book.png')}i.fa-kit{background-image:url('../images/icons/briefcase.png')}i.fa-leaf,.fa-sitemap{background-image:url('../images/icons/treeCurrent.png')}i.fa-list{background-image:url('../images/icons/view_list.png')}i.fa-list-alt,i.fa-export{background-image:url('../images/icons/application_add.png')}i.fa-lock{background-image:url('../images/icons/lock.png')}i.fa-manuscript{background-image:url('../images/icons/script.png')}i.fa-map{background-image:url('../images/icons/map.png')}i.fa-microfilm{background-image:url('../images/icons/film.png')}i.fa-minus-circle,i.fa-minus-sign{background-image:url('../images/icons/delete.png')}i.fa-mobile{background-image:url('../images/icons/phone.png')}i.fa-motionpicture{background-image:url('../images/icons/television.png')}i.fa-musicalscore{background-image:url('../images/icons/music.png')}i.fa-musicrecording{background-image:url('../images/icons/music.png')}i.fa-newspaper{background-image:url('../images/icons/newspaper.png')}i.fa-ok{background-image:url('../images/icons/tick.png')}i.fa-online{background-image:url('../images/icons/computer.png')}i.fa-painting{background-image:url('../images/icons/paintbrush.png')}i.fa-photo{background-image:url('../images/icons/photo.png')}i.fa-photonegative{background-image:url('../images/icons/film.png')}i.fa-physicalobject{background-image:url('../images/icons/box.png')}i.fa-plus{background-image:url('../images/icons/add.png')}i.fa-plus-circle{background-image:url('../images/icons/add.png')}i.fa-print{background-image:url('../images/icons/printer.png')}i.fa-qrcode{background-image:url('../images/icons/qrcode.png')}i.fa-remove{background-image:url('../images/icons/delete.png')}i.fa-renew{background-image:url('../images/icons/renew.png')}i.fa-renew-all{background-image:url('../images/icons/renewAll.png')}i.fa-report{background-image:url('../images/icons/report.png')}i.fa-rss{background-image:url('../images/icons/feed.png')}i.fa-save{background-image:url('../images/icons/disk.png')}i.fa-search{background-image:url('../images/icons/magnifier.png')}i.fa-sensorimage{background-image:url('../images/icons/photo.png')}i.fa-serial{background-image:url('../images/icons/page_white_stack.png')}i.fa-shopping-cart{background-image:url('../images/icons/cart.png')}i.fa-sign-in{background-image:url('../images/icons/door_in.png')}i.fa-sign-out{background-image:url('../images/icons/door_out.png')}i.fa-slide{background-image:url('../images/icons/film.png')}i.fa-software{background-image:url('../images/icons/drive_cd.png')}i.fa-soundcassette{background-image:url('../images/icons/sound.png')}i.fa-sounddisc{background-image:url('../images/icons/cd.png')}i.fa-soundrecording{background-image:url('../images/icons/sound.png')}i.fa-spinner{background-image:url('../images/icons/ajax_loading.gif')}i.fa-star{background-image:url('../images/icons/star.png')}i.fa-status-unknown{background-image:url('../images/icons/bullet_orange.png')}i.fa-suitcase{background-image:url('../images/icons/bookbag.png')}i.fa-tapecartridge{background-image:url('../images/icons/drive.png')}i.fa-tapecassette{background-image:url('../images/icons/drive.png')}i.fa-tapereel{background-image:url('../images/icons/film.png')}i.fa-transparency{background-image:url('../images/icons/film.png')}i.fa-trash,i.fa-trash-o{background-image:url('../images/icons/bin.png')}i.fa-tree{background-image:url('../images/icons/treeCurrent.png')}i.fa-tree-muted{background-image:url('../images/icons/treeMuted.png')}i.fa-unknown{background-image:url('../images/icons/page_white.png')}i.fa-usd{background-image:url('../images/icons/money_dollar.png')}i.fa-user{background-image:url('../images/icons/user.png')}i.fa-video{background-image:url('../images/icons/television.png')}i.fa-videocartridge{background-image:url('../images/icons/television.png')}i.fa-videocassette{background-image:url('../images/icons/television.png')}i.fa-videodisc{background-image:url('../images/icons/cd.png')}i.fa-videoreel{background-image:url('../images/icons/film.png')}i.fa-visual{background-image:url('../images/icons/view_visual.png')}body.rtl i.fa-external-link{background-image:url('../images/icons/link_go_rtl.png')}body.rtl i.fa-flag{background-image:url('../images/icons/flag_red_rtl.png')}#cart-empty-label i.fa-close{background-image:url('../images/icons/briefcase.png')}.searchHomeContent{float:none;margin:1em auto;width:90%}#advSearchForm .search{margin:0}.group .match{margin-top:.5em}.searchForm_lookfor,.searchForm_type{border-color:#619144}[name=searchForm]{margin:6px 8px 8px;padding:0}[name=searchForm] .clear-btn,[name=searchForm] .btn-primary,[name=searchForm] .form-control{font-size:14px;height:32px;padding:5px 8px}[name=searchForm] .clear-btn,[name=searchForm] .btn-primary[multiple],[name=searchForm] .form-control[multiple]{height:auto}@media (min-width:768px){[name=searchForm] .search-query{width:400px}}[name=searchForm] .nav-tabs{border-bottom:0;padding:0 6px}[name=searchForm] .nav-tabs li a{margin-bottom:-1px;border-bottom:0;padding-bottom:6px}[name=searchForm] .nav-tabs li a:hover{background:0 0;border-color:transparent;text-decoration:underline}[name=searchForm] .nav-tabs li.active a,[name=searchForm] .nav-tabs li.active a:hover{background:#FFF;border-color:#619144;border-bottom:0;text-decoration:none;z-index:5}body{background:#619144;font-size:13px}.container{background:#FFF;padding:0}.main .container{padding:0 4px 18px}a,.btn-link{color:#06C}a:hover,.btn-link:hover{color:#09F}.alert{padding:8px}.btn{padding:3px 5px 2px}.btn.btn-default{background:#eee;background-image:linear-gradient(#FFF, #ddd);border:1px solid #555;color:#222;text-shadow:0 1px 0 #FFF}.btn:not(.btn-default){font-weight:bold}.btn-danger,.btn-danger:hover{border-color:#6e110e}.btn-info,.btn-info:hover{border-color:#103743}.btn-primary,.btn-primary:hover{border-color:#456730}.btn-success,.btn-success:hover{border-color:#014701}.btn-warning,.btn-warning:hover{border-color:#683d00}#commentList .comment:nth-child(even){background:#eee}#dateVisColorSettings{stroke:#619144}#hierarchyRecord{background:#FFF}h2{margin:0 8px 8px}input[type=radio],input[type=checkbox]{margin:2px auto 0;padding:0 2px}.nav>li>a{padding:5px 10px}.nav-pills{display:table;margin:0 auto}.navbar{min-height:1px}.navbar-form{margin-top:5px;margin-bottom:5px}.pagination{display:table;margin:18px auto}.pagination>li>a,.pagination>li>span{padding:4px 12px 3px}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{background:#619144;border-color:#619144}.panel-heading{padding:0}.panel-heading a{cursor:pointer;display:inline-block;padding:6px;width:100%}.row:not(.top-row){padding:6px 4px;margin:0 -4px}.row>p{padding:0 1em}.sub-breadcrumb{padding:0 5px}.tab-content{padding:6px 8px;border:1px solid #eee;border-top:0}@media (max-width:767px){body{padding:6px}header{margin-top:0}.label,.result .format,.sidebar .format{font-size:85%}}@media (min-width:768px){.badge{font-size:85%;margin-top:1px}.label,.result .format,.sidebar .format{padding-top:.3em}.modal-dialog{width:650px}}header{margin-top:18px}header .fa.fa-bars{font-size:21px}header .navbar{border-radius:5px 5px 0 0;padding:0 10px}header .navbar .searchForm{display:none !important}header .navbar .navbar-brand{height:65px;width:170px;margin-top:5px;color:transparent;background-image:url('../images/vufind_logo.png');background-position:center center;background-repeat:no-repeat;background-size:contain}header .navbar .navbar-brand:active,header .navbar .navbar-brand:focus,header .navbar .navbar-brand:hover{color:transparent}header .navbar .navbar-brand.lang-ar{background-image:url('../images/vufind_logo_ar.png')}header .navbar .navbar-nav>li>a{padding:12px 6px}header .navbar .navbar-right{margin-top:12px}@media (max-width:767px){header .navbar .navbar-nav>li>a{padding:8px 24px}header .navbar .navbar-right{margin:0}}header .searchbox{background:linear-gradient(to bottom, #FFF, #EEE);display:block !important}header .searchbox .tab-content{border:0}header .searchbox .tab-content .navbar-text{margin:5px 10px 5px 0}@media (max-width:767px){header #header-collapse .navbar-right li{text-align:right}header .searchForm_type{margin-top:2px;margin-bottom:2px}}header .breadcrumb{border:1px solid #CCC;border-radius:0;border-width:1px 0;font-size:12px;margin-bottom:2px;padding:7px 20px 5px}footer{margin-bottom:36px}footer .container{border-radius:0 0 5px 5px;border-top:1px solid #ddd;padding-top:18px}footer hr{display:none}footer p{margin:0}footer ul{padding-left:30px}[id^=list].list-group .col-sm-9{margin:0}body.offcanvas .offcanvas-toggle{padding-bottom:18px;font-size:16px;background:#fff;box-shadow:0 0 2px #000;color:#619144}body.offcanvas .sidebar .list-group{color:#000}body.offcanvas.active .sidebar{color:#FFF}body.offcanvas.active .offcanvas-toggle{box-shadow:none}ul.random{list-style:none;padding:0;margin:0;text-align:justify}ul.random li{padding-bottom:10px}ul.random li img{margin:0 auto 1em}ul.random.image,ul.random.mixed{text-align:center}ul.random.image li img{margin:0 auto}#custom_recaptcha_widget{display:table}#custom_recaptcha_widget embed{display:none}#custom_recaptcha_widget #recaptcha_image{border:1px solid #000;padding:6px;margin:1em 0}#custom_recaptcha_widget #recaptcha_response_field{margin:0 .5em}#custom_recaptcha_widget>div>a{display:inline-block;float:left;margin:5px 10px 5px 0}.tagList button{margin-top:0;padding-top:0;padding-bottom:4px;font-size:95%;vertical-align:initial}.tagList button .fa-close{margin-top:3px}.bulkActionButtons{margin-bottom:6px}.result{padding:1rem;margin-left:-1.1rem}.result:nth-child(even){background-color:#eee}.result.embedded .getFull.expanded{margin-top:-6px;padding-top:.5rem;padding-bottom:.5rem}.result>p{padding:0 1em}.result .label{display:inline-block;margin-bottom:4px}.result .long-view .tab-content{background:#FFF}.result .media{margin:0}.result .row{padding:0}.result .savedLists{margin:0 0 4px;padding:4px 0 4px 6px}.result .savedLists ul{padding-left:18px}.search-controls label{text-align:left}@media (max-width:767px){.result .search-controls .form-inline{text-align:left}.search-controls{margin:4px -4px;padding:4px 0}}.sidebar .list-group{margin-bottom:5px}.sidebar .list-group label.list-group-item{padding-left:26px}.sidebar .list-group label.list-group-item input[type=checkbox]{margin-top:2px}.sidebar .list-group-item{padding:7px 10px 6px}.sidebar .list-group-item.active{color:#fff}.sidebar .list-group-item.active .badge{color:#E70}.sidebar .list-group-item.active,.sidebar .list-group-item.active:hover{background:#E70;border-color:#E70}.sidebar .list-group-item .badge a{color:#fff}.sidebar .slider-container{margin:4px auto 10px;width:95%}.sidebar .slider-container .slider-handle{background:#619144;opacity:1}.top-row .badge a{color:#fff}.top-row .badge a:hover{color:#A41915} \ No newline at end of file diff --git a/themes/bootstrap3/css/compiled.css b/themes/bootstrap3/css/compiled.css index bec95e356399a2a85d6a1856ee925a678f55b6b1..3a07d61068ed209eb262d07ee4647ef9d57f2857 100644 --- a/themes/bootstrap3/css/compiled.css +++ b/themes/bootstrap3/css/compiled.css @@ -5,4 +5,4 @@ *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label,.result .format,.sidebar .format{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#12538B;text-decoration:none}a:hover,a:focus{color:#092b47;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#265680}a.text-primary:hover,a.text-primary:focus{color:#1a3c59}.text-success{color:#3c763d}a.text-success:hover,a.text-success:focus{color:#2b542c}.text-info{color:#31708f}a.text-info:hover,a.text-info:focus{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover,a.text-warning:focus{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover,a.text-danger:focus{color:#843534}.bg-primary{color:#fff;background-color:#265680}a.bg-primary:hover,a.bg-primary:focus{background-color:#1a3c59}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin:0;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{border:0;background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:34px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:46px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0;min-height:34px}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:focus,.btn-default.focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .btn-default.dropdown-toggle{color:#fff;background-color:#333;border-color:#adadad}.btn-primary{color:#fff;background-color:#265680;border-color:#fff}.btn-primary:focus,.btn-primary.focus{color:#fff;background-color:#1a3c59;border-color:#bfbfbf}.btn-primary:hover{color:#fff;background-color:#1a3c59;border-color:#e0e0e0}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#1a3c59;border-color:#e0e0e0}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#fff;background-color:#12293d;border-color:#bfbfbf}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#265680;border-color:#fff}.btn-primary .badge{color:#265680;background-color:#fff}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .btn-primary.dropdown-toggle{color:#265680;background-color:#fff;border-color:#e0e0e0}.btn-success{color:#fff;background-color:#028302;border-color:#fff}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#015101;border-color:#bfbfbf}.btn-success:hover{color:#fff;background-color:#015101;border-color:#e0e0e0}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#015101;border-color:#e0e0e0}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#fff;background-color:#012e01;border-color:#bfbfbf}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#028302;border-color:#fff}.btn-success .badge{color:#028302;background-color:#fff}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .btn-success.dropdown-toggle{color:#028302;background-color:#fff;border-color:#e0e0e0}.btn-info{color:#fff;background-color:#1C5F74;border-color:#fff}.btn-info:focus,.btn-info.focus{color:#fff;background-color:#123d4b;border-color:#bfbfbf}.btn-info:hover{color:#fff;background-color:#123d4b;border-color:#e0e0e0}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#123d4b;border-color:#e0e0e0}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#fff;background-color:#0b262e;border-color:#bfbfbf}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#1C5F74;border-color:#fff}.btn-info .badge{color:#1C5F74;background-color:#fff}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .btn-info.dropdown-toggle{color:#1C5F74;background-color:#fff;border-color:#e0e0e0}.btn-warning{color:#fff;background-color:#A56100;border-color:#fff}.btn-warning:focus,.btn-warning.focus{color:#fff;background-color:#724300;border-color:#bfbfbf}.btn-warning:hover{color:#fff;background-color:#724300;border-color:#e0e0e0}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#724300;border-color:#e0e0e0}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#fff;background-color:#4e2e00;border-color:#bfbfbf}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#A56100;border-color:#fff}.btn-warning .badge{color:#A56100;background-color:#fff}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .btn-warning.dropdown-toggle{color:#A56100;background-color:#fff;border-color:#e0e0e0}.btn-danger{color:#fff;background-color:#A41915;border-color:#fff}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#77120f;border-color:#bfbfbf}.btn-danger:hover{color:#fff;background-color:#77120f;border-color:#e0e0e0}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#77120f;border-color:#e0e0e0}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#fff;background-color:#570d0b;border-color:#bfbfbf}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#A41915;border-color:#fff}.btn-danger .badge{color:#A41915;background-color:#fff}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .btn-danger.dropdown-toggle{color:#A41915;background-color:#fff;border-color:#e0e0e0}.btn-link{color:#12538B;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#092b47;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height, visibility;transition-property:height, visibility;-webkit-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;text-align:left;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,0.175);box-shadow:0 6px 12px rgba(0,0,0,0.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#265680}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#777}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-right-radius:0;border-top-left-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:normal;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#777;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#12538B}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#265680}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px 15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-right-radius:4px;border-top-left-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}}@media (min-width:768px){.navbar-left{float:left !important;float:left}.navbar-right{float:right !important;float:right;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#132531;border-color:#0a1319}.navbar-default .navbar-brand{color:#fff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#068139;background-color:transparent}.navbar-default .navbar-text{color:#fff}.navbar-default .navbar-nav>li>a{color:#fff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#fff;background-color:#068139}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#0a1319}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#fff;color:#132531}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#fff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#132531;background-color:#fff}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#fff;background-color:#068139}}.navbar-default .navbar-link{color:#fff}.navbar-default .navbar-link:hover{color:#132531}.navbar-default .btn-link{color:#fff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#132531}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#fff}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#fff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#12538B;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#092b47;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#fff;background-color:#265680;border-color:#265680;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#777;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#777;background-color:#fff;cursor:not-allowed}.label,.result .format,.sidebar .format{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:bold;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:hover,.label-default[href]:focus{background-color:#5e5e5e}.label-primary{background-color:#265680}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#1a3c59}.label-success{background-color:#028302}.label-success[href]:hover,.label-success[href]:focus{background-color:#015101}.label-info,.result .format,.sidebar .format{background-color:#1C5F74}.label-info[href]:hover,.label-info[href]:focus{background-color:#123d4b}.label-warning{background-color:#A56100}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#724300}.label-danger{background-color:#A41915}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#77120f}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;color:#fff;line-height:1;vertical-align:middle;white-space:nowrap;text-align:center;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#12538B;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px;padding-left:15px;padding-right:15px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#12538B}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.1);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#265680;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#028302}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#1C5F74}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#A56100}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#A41915}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{zoom:1;overflow:hidden}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item,.result.embedded .getFull.expanded,.result.embedded .loading{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{text-decoration:none;color:#555;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{background-color:#eee;color:#777;cursor:not-allowed}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#fff;background-color:#265680;border-color:#265680}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#93bcdf}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.05);box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-right-radius:0;border-top-left-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-left:15px;padding-right:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-left-radius:3px;border-bottom-right-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#265680}.panel-primary>.panel-heading{color:#fff;background-color:#265680;border-color:#265680}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#265680}.panel-primary>.panel-heading .badge{color:#265680;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#265680}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;left:0;bottom:0;height:100%;width:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.05);box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close,.group .group-close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:hidden;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);-ms-transform:translate(0, -25%);-o-transform:translate(0, -25%);transform:translate(0, -25%);-webkit-transition:-webkit-transform 0.3s ease-out;-moz-transition:-moz-transform 0.3s ease-out;-o-transition:-o-transform 0.3s ease-out;transition:transform 0.3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);-ms-transform:translate(0, 0);-o-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,0.5);box-shadow:0 3px 9px rgba(0,0,0,0.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,0.5);box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:12px;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;right:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:normal;letter-spacing:normal;line-break:auto;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;white-space:normal;word-break:normal;word-spacing:normal;word-wrap:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,0.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform 0.6s ease-in-out;-moz-transition:-moz-transform 0.6s ease-in-out;-o-transition:-o-transform 0.6s ease-in-out;transition:transform 0.6s ease-in-out;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;-moz-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0)}.carousel-control.left{background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:-o-linear-gradient(left, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;margin-top:-10px;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;line-height:1;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}/*! * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) - */@font-face{font-family:'FontAwesome';src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../../bootstrap3/css/fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{width:1px;height:1px;margin:-1px;clip:rect(0, 0, 0, 0);clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0 0 0 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0 0 0 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0, rgba(0,0,0,0.5) 100%);box-shadow:0 0 0 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:.5em;left:.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder,input:-ms-input-placeholder,textarea:-ms-input-placeholder,input::-ms-input-placeholder,textarea::-ms-input-placeholder,input::placeholder,textarea::placeholder{color:#888}.sr-only{clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only:focus{background-color:#fff;border-radius:4px;clip:auto;color:#132531;display:block;font-size:14px;height:50px;line-height:20px;padding:15px 15px;position:absolute;left:5px;top:5px;text-decoration:none;text-transform:none;width:auto;z-index:100000}.navbar-brand{font-size:20px}.group{position:relative;background:#eee;border-radius:4px;border:1px solid #c8c8c8;margin-top:0;margin-bottom:.5em !important}.group .add_search_link{display:inline-block;margin-top:4px}.group .group-close{position:absolute;top:.3em;right:.5em;opacity:.4;z-index:2}.group .search{margin-bottom:2px}.group .search .close{opacity:.8}@media (min-width:768px){.group{padding:10px 10px 10px 25px}.group [class^=col-]{padding-left:0}}@media (max-width:767px){.group .search .middle{float:left;width:90%}.group .group-close{top:.5em;right:1em;opacity:.6}}@media (max-width:991px){.group .form-control{max-width:none}}#groupPlaceHolder{display:block;padding:6px}.template-dir-eds.template-name-advanced legend{margin-bottom:0}.template-dir-eds.template-name-advanced .no-js .group:nth-child(n+3){display:none}.template-dir-eds.template-name-advanced .search .close a{margin-left:-2em}.alphabrowse{border-collapse:separate}.alphabrowse .lcc{width:20%}.alphabrowse .titles{width:10%;text-align:center}.alphabrowse tr.browse-match td{border-top:.2em solid #265680;border-bottom:.2em solid #265680}.alphabrowse tr.browse-match td:first-child{border-left:.2em solid #265680}.alphabrowse tr.browse-match td:last-child{border-right:.2em solid #265680}.autocomplete-results{position:absolute;margin:0;margin-top:2px;padding:0;border:1px solid lightgray;background-color:#fff;border-radius:4px;overflow:hidden;z-index:50}.autocomplete-results .item{display:block;margin:0;padding:.75rem 1.25rem;border-bottom:1px solid lightgray;cursor:pointer}.autocomplete-results .item:last-child{border:0}.autocomplete-results .item:hover{background-color:#e2edf6}.autocomplete-results .item.loading{background-color:#fff}.autocomplete-results .item.selected{background-color:#265680;color:#fff}.autocomplete-results .item small{display:block;color:darkgray}.fa-grid:before{content:"\f00a"}.fa-visual:before{content:"\f008"}.fa-x:before{content:"\f0f6"}.fa-atlas:before{content:"\f14e"}.fa-book:before{content:"\f02d"}.fa-braille:before{content:"\f0a6"}.fa-cdrom:before{content:"\f109"}.fa-chart:before{content:"\f012"}.fa-chipcartridge:before{content:"\f109"}.fa-collage:before{content:"\f03e"}.fa-disccartridge:before{content:"\f109"}.fa-drawing:before{content:"\f03e"}.fa-ebook:before{content:"\f0f6"}.fa-electronic:before{content:"\f1c6"}.fa-filmstrip:before{content:"\f008"}.fa-flashcard:before{content:"\f0e7"}.fa-floppydisk:before{content:"\f0c7"}.fa-globe:before{content:"\f0ac"}.fa-journal:before{content:"\f0f6"}.fa-kit:before{content:"\f0b1"}.fa-manuscript:before{content:"\f0f6"}.fa-map:before{content:"\f14e"}.fa-microfilm:before{content:"\f008"}.fa-motionpicture:before{content:"\f03d"}.fa-musicalscore:before{content:"\f001"}.fa-musicrecording:before{content:"\f001"}.fa-newspaper:before{content:"\f0f6"}.fa-online:before{content:"\f109"}.fa-painting:before{content:"\f03e"}.fa-photo:before{content:"\f03e"}.fa-photonegative:before{content:"\f03e"}.fa-physicalobject:before{content:"\f187"}.fa-print:before{content:"\f03e"}.fa-sensorimage:before{content:"\f03e"}.fa-serial:before{content:"\f0f6"}.fa-slide:before{content:"\f008"}.fa-software:before{content:"\f109"}.fa-soundcassette:before{content:"\f025"}.fa-sounddisc:before{content:"\f109"}.fa-soundrecording:before{content:"\f025"}.fa-tapecartridge:before{content:"\f109"}.fa-tapecassette:before{content:"\f025"}.fa-tapereel:before{content:"\f008"}.fa-transparency:before{content:"\f008"}.fa-unknown:before{content:"\f128"}.fa-video:before{content:"\f03d"}.fa-videocartridge:before{content:"\f03d"}.fa-videocassette:before{content:"\f03d"}.fa-videodisc:before{content:"\f109"}.fa-videoreel:before{content:"\f03d"}.hierarchy-tree .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-style:normal;font-weight:normal;cursor:pointer;text-decoration:inherit;speak:none}.hierarchy-tree .jstree-open>.jstree-ocl:before{content:"\f0d7"}.hierarchy-tree .jstree-closed>.jstree-ocl:before{content:"\f0da"}.hierarchy-tree .jstree-leaf>.jstree-ocl:before{content:" "}.hierarchy-tree .jstree-icon{width:16px;color:#000}.hierarchy-tree .jstree-anchor{padding:2px 5px;white-space:nowrap}.hierarchy-tree .jstree-container-ul,.hierarchy-tree .jstree-children{padding-left:16px}.hierarchy-tree .jstree-initial-node{display:none}.hierarchy-tree .jstree-clicked{color:#fff;background-color:#265680}.hierarchy-tree .jstree-clicked .jstree-icon{color:#fff}.hierarchy-tree .jstree-search a{font-style:italic;color:#8b0000;font-weight:bold}#hierarchyTreeHolder{overflow-x:hidden;border-right:1px solid #eee}#hierarchyTree .currentHierarchy>a,#hierarchyTree .currentRecord a{font-weight:bold;color:#000}.facet .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-weight:normal;font-style:normal;text-decoration:inherit;cursor:pointer;speak:none}.facet .jstree-default .jstree-open>.jstree-ocl:before{content:"\f0d7"}.facet .jstree-default .jstree-closed>.jstree-ocl:before{content:"\f0da"}.facet .jstree-default .jstree-leaf>.jstree-ocl:before{content:" "}.jstree-facet li span.main{display:block;padding-left:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jstree-facet .jstree-container-ul{padding:0}.jstree-facet .jstree-container-ul>li.active,.jstree-facet .jstree-container-ul>li.active a.jstree-anchor{background-color:#265680;color:#fff}li.jstree-facet,li.jstree-node{list-style:none}li.jstree-facet .badge{cursor:text}li.jstree-facet ul{padding-left:20px}.lightbox-only{display:none}#modal .lightbox-only{display:initial}#modal{background-color:rgba(0,0,0,0.2)}#modal .modal-content>.close{position:absolute;right:-50px;top:0;z-index:2;font-size:32pt;color:#fff;opacity:.7}#modal .modal-content>.close:hover{opacity:1}#modal .modal-body h1,#modal .modal-body h2{margin-top:.3rem;margin-bottom:1.3rem}#modal .cart-controls .btn{margin-bottom:4px}#modal .cart-controls .checkbox{padding-bottom:1em}#modal .cart-controls~hr{margin-top:0}.lightbox-scroll{overflow-y:auto}.offcanvas-overlay,.offcanvas-toggle{display:none}@media screen and (max-width:767px){body.offcanvas{overflow-x:hidden}body.offcanvas .sidebar{position:fixed;height:100%;top:0;width:75%;padding-left:0;padding-right:0;overflow-y:auto}body.offcanvas .sidebar h4{padding-left:12px}body.offcanvas .sidebar .checkbox{margin-left:32px}body.offcanvas .sidebar .list-group,body.offcanvas .sidebar .list-group-item{border-left:0;border-right:0;border-radius:0 !important}body.offcanvas.active{overflow-y:hidden}body.offcanvas.offcanvas-left{padding-left:15px}body.offcanvas.offcanvas-left .main{background:#FFF}body.offcanvas.offcanvas-left.active{margin-left:75%;margin-right:-75%}body.offcanvas.offcanvas-left.active .sidebar{left:0}body.offcanvas.offcanvas-left.active .offcanvas-overlay{right:-75%}body.offcanvas.offcanvas-left.active .offcanvas-toggle{left:75%}body.offcanvas.offcanvas-left .sidebar{left:-75%}body.offcanvas.offcanvas-left .offcanvas-overlay{right:-100%}body.offcanvas.offcanvas-left .offcanvas-toggle{border-radius:0 3px 3px 0;left:0}body.offcanvas.offcanvas-right{padding-right:15px}body.offcanvas.offcanvas-right .main>.container{background:#FFF}body.offcanvas.offcanvas-right.active{margin-left:-75%;margin-right:75%}body.offcanvas.offcanvas-right.active .sidebar{right:0}body.offcanvas.offcanvas-right.active .offcanvas-overlay{left:-75%}body.offcanvas.offcanvas-right.active .offcanvas-toggle{right:75%}body.offcanvas.offcanvas-right .sidebar{right:-75%}body.offcanvas.offcanvas-right .offcanvas-overlay{left:-100%}body.offcanvas.offcanvas-right .offcanvas-toggle{border-radius:3px 0 0 3px;right:0}body.offcanvas .offcanvas-overlay{display:block;position:fixed;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.3);z-index:3}body.offcanvas .offcanvas-toggle{display:block;position:fixed;top:50%;width:calc(25px);padding:20px 0;background:#265680;color:#EEE;text-align:center;z-index:5}body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle,body.offcanvas .offcanvas-toggle *{cursor:pointer}body.offcanvas,body.offcanvas .sidebar,body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle{-webkit-transition:all .25s ease-out;-o-transition:all .25s ease-out;transition:all .25s ease-out}}.citation .pace-car th,.citation .pace-car td{border:0;padding:0}.citation th{text-align:right}.item-notes ul{padding-left:2rem}.recordcover{max-height:300px}.tagList .tag{display:inline-block;margin:0 1px 1px;padding:6px 6px;font-size:14px;line-height:1.42857143;border-radius:4px}.tagList .tag.selected{background-color:#265680}.tagList .tag.selected a{color:#fff}.tagList .tag.selected .badge{color:#222;background-color:#fff}.tagList .tag.selected .badge:hover{color:#a94442}.tagList .tag .badge .fa{width:12px}.tagList button{border:0}.tagList .tag-form{display:inline}.tagList.loggedin .tag:not(.selected) .badge:hover{background-color:#028302}.subject-line:hover{color:#999}.subject-line:hover a{color:#092b47}.subject-line a:hover~a{color:#999;text-decoration:none}.record .format::after{content:", "}.result .record .format::after,.record .format:last-child::after{content:""}.marc-row-LEADER,.marc-row-006,.marc-row-007,.marc-row-008{white-space:pre-wrap}.bulkActionButtons label{display:inline-block}.bulkActionButtons label input{margin-top:2px}@media (max-width:767px){.grid{min-height:250px}}.result{padding-top:1.5rem}.result .checkbox{float:left;padding-right:.5rem}.result .media,.result .media-body{overflow:visible}.result .media-body .row{margin-left:0;margin-right:0}.result .media{margin-top:0;margin-bottom:1rem}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media-left,.result .media-right{padding:0;text-align:center}.result .media-left a,.result .media-right a{display:inline-block;text-align:center}.result .media-left img,.result .media-right img{max-width:none;max-height:300px}.result .media-left.small a,.result .media-right.small a,.result .media-left.small>img,.result .media-right.small>img{width:60px}.result .media-left.small a img,.result .media-right.small a img,.result .media-left.small>img img,.result .media-right.small>img img{max-width:60px}.result .media-left.medium a,.result .media-right.medium a,.result .media-left.medium>img,.result .media-right.medium>img{width:100px}.result .media-left.medium a img,.result .media-right.medium a img,.result .media-left.medium>img img,.result .media-right.medium>img img{max-width:100px}.result .media-left.large a,.result .media-right.large a,.result .media-left.large>img,.result .media-right.large>img{width:160px}.result .media-left.large a img,.result .media-right.large a img,.result .media-left.large>img img,.result .media-right.large>img img{max-width:160px}.result .media-left{padding-left:10px}.result .media-right{padding-right:10px}.result .title{font-weight:bold}.result .list-tab-content.record .img-col{display:none}.result .list-tab-content.record .info-col{width:100%}@media (max-width:767px){.result a{text-decoration:underline}}@media (max-width:530px){.result .checkbox{padding:0}.result .media-left{padding-right:0}}.result.embedded .getFull{display:block;margin-left:1.5rem;padding-right:30px;border-left:1px solid transparent}.result.embedded .getFull.expanded{margin-top:-11px;margin-left:.75rem;padding-left:.75rem;border-top-left-radius:4px;border-top-right-radius:4px}.result.embedded .getFull.expanded::before{content:'\25BC';position:absolute;right:15px;color:#555}.result.embedded .loading{margin-left:.75rem;padding:1rem;background:#fff}.result.embedded .long-view{margin-left:.75rem;padding:.5rem;border:1px solid #ddd;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.result.embedded .long-view .tab-content{padding:0}.result.embedded .list-tabs{margin-bottom:0}.result.embedded .list-tab-toggle{cursor:pointer}.result.embedded .list-tab-content{padding:1rem}.search-controls .alert{margin-bottom:0}.searchtools a{padding:0 .5em}.title-in-heading{font-size:inherit;font-style:italic}.wikipedia img{margin-right:1rem}.narrow-toggle{text-align:center}.sidebar label:not(.list-group-item){margin-left:20px}.sidebar .list-group.facet .list-group-item.title{cursor:pointer}.sidebar .list-group.facet .list-group-item.title.collapsed{border-radius:4px}.sidebar .list-group.facet .list-group-item.title.collapsed:after{content:'\25BC'}.sidebar .list-group.facet .list-group-item.title:after{content:'\25B2';float:right}.sidebar .collapse .list-group-item,.sidebar .collapsing .list-group-item{border-top-left-radius:0;border-top-right-radius:0}.sidebar .collapse .list-group-item[id^=more],.sidebar .collapsing .list-group-item[id^=more]{border-bottom-left-radius:4px;border-bottom-right-radius:4px}.sidebar #side-collapse-publishDate .list-group-item{border-bottom-left-radius:4px;border-bottom-right-radius:4px}label.list-group-item{margin-top:0;padding-left:35px;font-weight:normal;border-radius:0}.list-group-item.title{font-weight:bold}.sidebar .facet a{text-decoration:none}.top-row .applied{font-weight:bold}.top-row .applied:hover{color:#a94442}.top-row .applied:hover .fa.fa-check:before{content:"\f00d"}.full-facet-list{margin-top:1rem}#similar-items-carousel .carousel-indicators{bottom:0}#similar-items-carousel .carousel-indicators li{width:8px;height:8px;margin:2px;background-color:rgba(255,255,255,0.3);border-color:#222}#similar-items-carousel .hover-overlay{position:relative;display:block;min-width:150px;min-height:200px;margin:auto;text-align:center}#similar-items-carousel .hover-overlay img{max-width:100%;margin:10px 0}#similar-items-carousel .hover-overlay .content{position:absolute;top:0;left:0;display:none;width:100%;height:100%;padding:.5em .5em 0;color:#fff;background-color:rgba(0,0,0,0.5)}#similar-items-carousel .hover-overlay:hover .content{display:block}#similar-items-carousel .item{padding:0 4em}.slider-container{padding:4px 10px;text-align:center}.slider-container .slider.slider-horizontal{width:100%}.slider-container .slider-track{background:#777;box-shadow:inset 0 1px 0 rgba(0,0,0,0.4)}.slider-container .slider-handle{background:#265680;background-image:none;border:1px solid #265680;box-shadow:none;opacity:.9}.slider-container .slider-handle:hover,.slider-container .slider-handle:active,.slider-container .slider-handle:focus{opacity:1;background:#FFF;border-color:#777}.slider-container .slider-handle:active,.slider-container .slider-handle:focus{border-color:#265680}.slider-container .slider-selection{background:#CCC;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.3)}.slider-container input{display:none}.alert.alert-info a{text-decoration:underline}.btn.disabled:active,.btn.disabled:focus,.btn.disabled:hover{color:#000}header .dropdown form{display:none}.list-unstyled{margin:0}.highlight,mark{background:#ff6;padding:.1em .2em}.icon-bar{background-color:#888}img{max-width:100%}.popover{width:250px}.sub-breadcrumb{padding:5px 10px;white-space:nowrap}.sub-breadcrumb li{display:inline-block}.sub-breadcrumb li+li:before{padding-left:5px;padding-right:5px;color:#ccc;content:"/\00a0"}.tab-content{padding:4px}@media (max-width:991px){header .container.navbar{margin-bottom:0}.searchForm{margin-top:0}}@media (min-width:768px){h2{font-size:23px;font-weight:normal}h3{font-size:20px;font-weight:normal}.form-control{max-width:400px}}@media (max-width:767px){h2{font-size:20px}h3{font-size:16px}.searchForm{padding-top:0}}.has-error{margin-bottom:0}.sms-error{margin-bottom:0}.sms-error .help-block,.sms-error .control-label,.sms-error .radio,.sms-error .checkbox,.sms-error .radio-inline,.sms-error .checkbox-inline,.sms-error.radio label,.sms-error.checkbox label,.sms-error.radio-inline label,.sms-error.checkbox-inline label{color:#a94442}.sms-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.sms-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.sms-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.sms-error .form-control-feedback{color:#a94442}.help-block.with-errors{margin:0;padding-top:6px;padding-bottom:6px}.help-block.with-errors:empty{padding:0}.badge a{color:#fff}.browse.list-group .list-group-item{word-wrap:break-word}.browse.list-group .list-group-item.view-record{padding:2px 4px;font-size:85%;text-align:right;border-top:0}.cart-controls .checkbox{line-height:2.5em;padding-right:1em}#dateVisColorSettings{background-color:#fff;fill:#eaeaea;outline-color:#c38835;stroke:#265680}.table{table-layout:fixed;word-wrap:break-word}.node{position:absolute;box-sizing:content-box;margin:-1px;overflow:hidden;font:10px sans-serif;line-height:12px;border:1px solid white}.node div{margin-top:0}.toplevel{border:2px solid black}.node .label{position:absolute;bottom:0;left:0;min-height:1px;padding:2px 4px;font-size:85%;background-color:rgba(0,0,0,0.5);border-radius:0;text-shadow:none}.notalabel{color:#000}#viz-instructions{padding-top:600px}span[class^="services-"],span[class*=" services-"] span::before{content:", "}span[class^="services-"],span[class*=" services-"] span:first-of-type::before{content:""} \ No newline at end of file + */@font-face{font-family:'FontAwesome';src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?v=4.6.3');src:url('../../bootstrap3/css/fonts/fontawesome-webfont.eot?#iefix&v=4.6.3') format('embedded-opentype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff2?v=4.6.3') format('woff2'),url('../../bootstrap3/css/fonts/fontawesome-webfont.woff?v=4.6.3') format('woff'),url('../../bootstrap3/css/fonts/fontawesome-webfont.ttf?v=4.6.3') format('truetype'),url('../../bootstrap3/css/fonts/fontawesome-webfont.svg?v=4.6.3#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.sr-only{width:1px;height:1px;margin:-1px;clip:rect(0, 0, 0, 0);clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.btn:focus{outline:dotted 2px #000}div.active:focus{outline:dotted 1px #000}a:focus{outline:dotted 1px #000}.close:hover,.close:focus{outline:dotted 1px #000}.nav>li>a:hover,.nav>li>a:focus{outline:dotted 1px #000}.carousel-indicators li,.carousel-indicators li.active{height:18px;width:18px;border-width:2px;position:relative;box-shadow:0 0 0 1px #808080}.carousel-indicators.active li{background-color:rgba(100,149,253,0.6)}.carousel-indicators.active li.active{background-color:white}.carousel-tablist-highlight{display:block;position:absolute;outline:2px solid transparent;background-color:transparent;box-shadow:0 0 0 1px transparent}.carousel-tablist-highlight.focus{outline:2px solid #6495ED;background-color:rgba(0,0,0,0.4)}a.carousel-control:focus{outline:2px solid #6495ED;background-image:linear-gradient(to right, transparent 0, rgba(0,0,0,0.5) 100%);box-shadow:0 0 0 1px #000000}.carousel-pause-button{position:absolute;top:-30em;left:-300em;display:block}.carousel-pause-button.focus{top:.5em;left:.5em}.carousel:hover .carousel-caption,.carousel.contrast .carousel-caption{background-color:rgba(0,0,0,0.5);z-index:10}.alert-success{color:#2d4821}.alert-info{color:#214c62}.alert-warning{color:#6c4a00;background-color:#f9f1c6}.alert-danger{color:#d2322d}.alert-danger:hover{color:#a82824}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder,input:-ms-input-placeholder,textarea:-ms-input-placeholder,input::-ms-input-placeholder,textarea::-ms-input-placeholder,input::placeholder,textarea::placeholder{color:#888}.sr-only{clip:rect(1px, 1px, 1px, 1px);position:absolute;width:auto;height:auto;margin:0;padding:0;overflow:hidden;border:0}.sr-only:focus{background-color:#fff;border-radius:4px;clip:auto;color:#132531;display:block;font-size:14px;height:50px;line-height:20px;padding:15px 15px;position:absolute;left:5px;top:5px;text-decoration:none;text-transform:none;width:auto;z-index:100000}.navbar-brand{font-size:20px}.group{position:relative;background:#eee;border-radius:4px;border:1px solid #c8c8c8;margin-top:0;margin-bottom:.5em !important}.group .add_search_link{display:inline-block;margin-top:4px}.group .group-close{position:absolute;top:.3em;right:.5em;opacity:.4;z-index:2}.group .search{margin-bottom:2px}.group .search .close{opacity:.8}@media (min-width:768px){.group{padding:10px 10px 10px 25px}.group [class^=col-]{padding-left:0}}@media (max-width:767px){.group .search .middle{float:left;width:90%}.group .group-close{top:.5em;right:1em;opacity:.6}}@media (max-width:991px){.group .form-control{max-width:none}}#groupPlaceHolder{display:block;padding:6px}.template-dir-eds.template-name-advanced legend{margin-bottom:0}.template-dir-eds.template-name-advanced .no-js .group:nth-child(n+3){display:none}.template-dir-eds.template-name-advanced .search .close a{margin-left:-2em}.alphabrowse{border-collapse:separate}.alphabrowse .lcc{width:20%}.alphabrowse .titles{width:10%;text-align:center}.alphabrowse tr.browse-match td{border-top:.2em solid #265680;border-bottom:.2em solid #265680}.alphabrowse tr.browse-match td:first-child{border-left:.2em solid #265680}.alphabrowse tr.browse-match td:last-child{border-right:.2em solid #265680}.autocomplete-results{position:absolute;margin:0;margin-top:2px;padding:0;border:1px solid lightgray;background-color:#fff;border-radius:4px;overflow:hidden;z-index:50}.autocomplete-results .item{display:block;margin:0;padding:.75rem 1.25rem;border-bottom:1px solid lightgray;cursor:pointer}.autocomplete-results .item:last-child{border:0}.autocomplete-results .item:hover{background-color:#e2edf6}.autocomplete-results .item.loading{background-color:#fff}.autocomplete-results .item.selected{background-color:#265680;color:#fff}.autocomplete-results .item small{display:block;color:darkgray}.fa-grid:before{content:"\f00a"}.fa-visual:before{content:"\f008"}.fa-x:before{content:"\f0f6"}.fa-atlas:before{content:"\f14e"}.fa-book:before{content:"\f02d"}.fa-braille:before{content:"\f0a6"}.fa-cdrom:before{content:"\f109"}.fa-chart:before{content:"\f012"}.fa-chipcartridge:before{content:"\f109"}.fa-collage:before{content:"\f03e"}.fa-disccartridge:before{content:"\f109"}.fa-drawing:before{content:"\f03e"}.fa-ebook:before{content:"\f0f6"}.fa-electronic:before{content:"\f1c6"}.fa-filmstrip:before{content:"\f008"}.fa-flashcard:before{content:"\f0e7"}.fa-floppydisk:before{content:"\f0c7"}.fa-globe:before{content:"\f0ac"}.fa-journal:before{content:"\f0f6"}.fa-kit:before{content:"\f0b1"}.fa-manuscript:before{content:"\f0f6"}.fa-map:before{content:"\f14e"}.fa-microfilm:before{content:"\f008"}.fa-motionpicture:before{content:"\f03d"}.fa-musicalscore:before{content:"\f001"}.fa-musicrecording:before{content:"\f001"}.fa-newspaper:before{content:"\f0f6"}.fa-online:before{content:"\f109"}.fa-painting:before{content:"\f03e"}.fa-photo:before{content:"\f03e"}.fa-photonegative:before{content:"\f03e"}.fa-physicalobject:before{content:"\f187"}.fa-print:before{content:"\f03e"}.fa-sensorimage:before{content:"\f03e"}.fa-serial:before{content:"\f0f6"}.fa-slide:before{content:"\f008"}.fa-software:before{content:"\f109"}.fa-soundcassette:before{content:"\f025"}.fa-sounddisc:before{content:"\f109"}.fa-soundrecording:before{content:"\f025"}.fa-tapecartridge:before{content:"\f109"}.fa-tapecassette:before{content:"\f025"}.fa-tapereel:before{content:"\f008"}.fa-transparency:before{content:"\f008"}.fa-unknown:before{content:"\f128"}.fa-video:before{content:"\f03d"}.fa-videocartridge:before{content:"\f03d"}.fa-videocassette:before{content:"\f03d"}.fa-videodisc:before{content:"\f109"}.fa-videoreel:before{content:"\f03d"}.hierarchy-tree .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-style:normal;font-weight:normal;cursor:pointer;text-decoration:inherit;speak:none}.hierarchy-tree .jstree-open>.jstree-ocl:before{content:"\f0d7"}.hierarchy-tree .jstree-closed>.jstree-ocl:before{content:"\f0da"}.hierarchy-tree .jstree-leaf>.jstree-ocl:before{content:" "}.hierarchy-tree .jstree-icon{width:16px;color:#000}.hierarchy-tree .jstree-anchor{padding:2px 5px;white-space:nowrap}.hierarchy-tree .jstree-container-ul,.hierarchy-tree .jstree-children{padding-left:16px}.hierarchy-tree .jstree-initial-node{display:none}.hierarchy-tree .jstree-clicked{color:#fff;background-color:#265680}.hierarchy-tree .jstree-clicked .jstree-icon{color:#fff}.hierarchy-tree .jstree-search a{font-style:italic;color:#8b0000;font-weight:bold}#hierarchyTreeHolder{overflow-x:hidden;border-right:1px solid #eee}#hierarchyTree .currentHierarchy>a,#hierarchyTree .currentRecord a{font-weight:bold;color:#000}.facet .jstree-ocl:before{float:left;width:10px;padding:0;margin-left:-10px;font-family:'FontAwesome';font-weight:normal;font-style:normal;text-decoration:inherit;cursor:pointer;speak:none}.facet .jstree-default .jstree-open>.jstree-ocl:before{content:"\f0d7"}.facet .jstree-default .jstree-closed>.jstree-ocl:before{content:"\f0da"}.facet .jstree-default .jstree-leaf>.jstree-ocl:before{content:" "}.jstree-facet li span.main{display:block;padding-left:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.jstree-facet .jstree-container-ul{padding:0}.jstree-facet .jstree-container-ul>li.active,.jstree-facet .jstree-container-ul>li.active a.jstree-anchor{background-color:#265680;color:#fff}li.jstree-facet,li.jstree-node{list-style:none}li.jstree-facet .badge{cursor:text}li.jstree-facet ul{padding-left:20px}.lightbox-only{display:none}#modal .lightbox-only{display:initial}#modal{background-color:rgba(0,0,0,0.2)}#modal .modal-content>.close{position:absolute;right:-50px;top:0;z-index:2;font-size:32pt;color:#fff;opacity:.7}#modal .modal-content>.close:hover{opacity:1}#modal .modal-body h1,#modal .modal-body h2{margin-top:.3rem;margin-bottom:1.3rem}#modal .cart-controls .btn{margin-bottom:4px}#modal .cart-controls .checkbox{padding-bottom:1em}#modal .cart-controls~hr{margin-top:0}.lightbox-scroll{overflow-y:auto}.offcanvas-overlay,.offcanvas-toggle{display:none}@media screen and (max-width:767px){body.offcanvas{overflow-x:hidden}body.offcanvas .sidebar{position:fixed;height:100%;top:0;width:75%;padding-left:0;padding-right:0;overflow-y:auto}body.offcanvas .sidebar h4{padding-left:12px}body.offcanvas .sidebar .checkbox{margin-left:32px}body.offcanvas .sidebar .list-group,body.offcanvas .sidebar .list-group-item{border-left:0;border-right:0;border-radius:0 !important}body.offcanvas.active{overflow-y:hidden}body.offcanvas.offcanvas-left{padding-left:15px}body.offcanvas.offcanvas-left .main{background:#FFF}body.offcanvas.offcanvas-left.active{margin-left:75%;margin-right:-75%}body.offcanvas.offcanvas-left.active .sidebar{left:0}body.offcanvas.offcanvas-left.active .offcanvas-overlay{right:-75%}body.offcanvas.offcanvas-left.active .offcanvas-toggle{left:75%}body.offcanvas.offcanvas-left .sidebar{left:-75%}body.offcanvas.offcanvas-left .offcanvas-overlay{right:-100%}body.offcanvas.offcanvas-left .offcanvas-toggle{border-radius:0 3px 3px 0;left:0}body.offcanvas.offcanvas-right{padding-right:15px}body.offcanvas.offcanvas-right .main>.container{background:#FFF}body.offcanvas.offcanvas-right.active{margin-left:-75%;margin-right:75%}body.offcanvas.offcanvas-right.active .sidebar{right:0}body.offcanvas.offcanvas-right.active .offcanvas-overlay{left:-75%}body.offcanvas.offcanvas-right.active .offcanvas-toggle{right:75%}body.offcanvas.offcanvas-right .sidebar{right:-75%}body.offcanvas.offcanvas-right .offcanvas-overlay{left:-100%}body.offcanvas.offcanvas-right .offcanvas-toggle{border-radius:3px 0 0 3px;right:0}body.offcanvas .offcanvas-overlay{display:block;position:fixed;top:0;width:100%;height:100%;background-color:rgba(0,0,0,0.3);z-index:3}body.offcanvas .offcanvas-toggle{display:block;position:fixed;top:50%;width:calc(25px);padding:20px 0;background:#265680;color:#EEE;text-align:center;z-index:5}body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle,body.offcanvas .offcanvas-toggle *{cursor:pointer}body.offcanvas,body.offcanvas .sidebar,body.offcanvas .offcanvas-overlay,body.offcanvas .offcanvas-toggle{-webkit-transition:all .25s ease-out;-o-transition:all .25s ease-out;transition:all .25s ease-out}}.citation .pace-car th,.citation .pace-car td{border:0;padding:0}.citation th{text-align:right}.item-notes ul{padding-left:2rem}.recordcover{max-height:300px}.tagList .tag{display:inline-block;margin:0 1px 1px;padding:6px 6px;font-size:14px;line-height:1.42857143;border-radius:4px}.tagList .tag.selected{background-color:#265680}.tagList .tag.selected a{color:#fff}.tagList .tag.selected .badge{color:#222;background-color:#fff}.tagList .tag.selected .badge:hover{color:#a94442}.tagList .tag .badge .fa{width:12px}.tagList button{border:0}.tagList .tag-form{display:inline}.tagList.loggedin .tag:not(.selected) .badge:hover{background-color:#028302}.subject-line:hover{color:#999}.subject-line:hover a{color:#092b47}.subject-line a:hover~a{color:#999;text-decoration:none}.record .format::after{content:", "}.result .record .format::after,.record .format:last-child::after{content:""}.marc-row-LEADER,.marc-row-006,.marc-row-007,.marc-row-008{white-space:pre-wrap}.bulkActionButtons label{display:inline-block}.bulkActionButtons label input{margin-top:2px}@media (max-width:767px){.grid{min-height:250px}}.result{padding-top:1.5rem}.result .checkbox{float:left;padding-right:.5rem}.result .media,.result .media-body{overflow:visible}.result .media-body .row{margin-left:0;margin-right:0}.result .media{margin-top:0;margin-bottom:1rem}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media:before,.result .media:after{content:" ";display:table}.result .media:after{clear:both}.result .media-left,.result .media-right{padding:0;text-align:center}.result .media-left a,.result .media-right a{display:inline-block;text-align:center}.result .media-left img,.result .media-right img{max-width:none;max-height:300px}.result .media-left.small a,.result .media-right.small a,.result .media-left.small>img,.result .media-right.small>img{width:60px}.result .media-left.small a img,.result .media-right.small a img,.result .media-left.small>img img,.result .media-right.small>img img{max-width:60px}.result .media-left.medium a,.result .media-right.medium a,.result .media-left.medium>img,.result .media-right.medium>img{width:100px}.result .media-left.medium a img,.result .media-right.medium a img,.result .media-left.medium>img img,.result .media-right.medium>img img{max-width:100px}.result .media-left.large a,.result .media-right.large a,.result .media-left.large>img,.result .media-right.large>img{width:160px}.result .media-left.large a img,.result .media-right.large a img,.result .media-left.large>img img,.result .media-right.large>img img{max-width:160px}.result .media-left{padding-left:10px}.result .media-right{padding-right:10px}.result .title{font-weight:bold}.result .list-tab-content.record .img-col{display:none}.result .list-tab-content.record .info-col{width:100%}@media (max-width:767px){.result a{text-decoration:underline}}@media (max-width:530px){.result .checkbox{padding:0}.result .media-left{padding-right:0}}.result.embedded .getFull{display:block;margin-left:1.5rem;padding-right:30px;border-left:1px solid transparent}.result.embedded .getFull.expanded{margin-top:-11px;margin-left:.75rem;padding-left:.75rem;border-top-left-radius:4px;border-top-right-radius:4px}.result.embedded .getFull.expanded::before{content:'\25BC';position:absolute;right:15px;color:#555}.result.embedded .loading{margin-left:.75rem;padding:1rem;background:#fff}.result.embedded .long-view{margin-left:.75rem;padding:.5rem;border:1px solid #ddd;background-color:#fff;border-bottom-left-radius:4px;border-bottom-right-radius:4px}.result.embedded .long-view .tab-content{padding:0}.result.embedded .list-tabs{margin-bottom:0}.result.embedded .list-tab-toggle{cursor:pointer}.result.embedded .list-tab-content{padding:1rem}.search-controls .alert{margin-bottom:0}.searchtools a{padding:0 .5em}.title-in-heading{font-size:inherit;font-style:italic}.wikipedia img{margin-right:1rem}.geoItem{font-size:.9em;margin:0 0 10px}.narrow-toggle{text-align:center}.sidebar label:not(.list-group-item){margin-left:20px}.sidebar .list-group.facet .list-group-item.title{cursor:pointer}.sidebar .list-group.facet .list-group-item.title.collapsed{border-radius:4px}.sidebar .list-group.facet .list-group-item.title.collapsed:after{content:'\25BC'}.sidebar .list-group.facet .list-group-item.title:after{content:'\25B2';float:right}.sidebar .collapse .list-group-item,.sidebar .collapsing .list-group-item{border-top-left-radius:0;border-top-right-radius:0}.sidebar .collapse .list-group-item[id^=more],.sidebar .collapsing .list-group-item[id^=more]{border-bottom-left-radius:4px;border-bottom-right-radius:4px}.sidebar #side-collapse-publishDate .list-group-item{border-bottom-left-radius:4px;border-bottom-right-radius:4px}label.list-group-item{margin-top:0;padding-left:35px;font-weight:normal;border-radius:0}.list-group-item.title{font-weight:bold}.sidebar .facet a{text-decoration:none}.top-row .applied{font-weight:bold}.top-row .applied:hover{color:#a94442}.top-row .applied:hover .fa.fa-check:before{content:"\f00d"}.full-facet-list{margin-top:1rem}#similar-items-carousel .carousel-indicators{bottom:0}#similar-items-carousel .carousel-indicators li{width:8px;height:8px;margin:2px;background-color:rgba(255,255,255,0.3);border-color:#222}#similar-items-carousel .hover-overlay{position:relative;display:block;min-width:150px;min-height:200px;margin:auto;text-align:center}#similar-items-carousel .hover-overlay img{max-width:100%;margin:10px 0}#similar-items-carousel .hover-overlay .content{position:absolute;top:0;left:0;display:none;width:100%;height:100%;padding:.5em .5em 0;color:#fff;background-color:rgba(0,0,0,0.5)}#similar-items-carousel .hover-overlay:hover .content{display:block}#similar-items-carousel .item{padding:0 4em}.slider-container{padding:4px 10px;text-align:center}.slider-container .slider.slider-horizontal{width:100%}.slider-container .slider-track{background:#777;box-shadow:inset 0 1px 0 rgba(0,0,0,0.4)}.slider-container .slider-handle{background:#265680;background-image:none;border:1px solid #265680;box-shadow:none;opacity:.9}.slider-container .slider-handle:hover,.slider-container .slider-handle:active,.slider-container .slider-handle:focus{opacity:1;background:#FFF;border-color:#777}.slider-container .slider-handle:active,.slider-container .slider-handle:focus{border-color:#265680}.slider-container .slider-selection{background:#CCC;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.3)}.slider-container input{display:none}.alert.alert-info a{text-decoration:underline}.btn.disabled:active,.btn.disabled:focus,.btn.disabled:hover{color:#000}header .dropdown form{display:none}.list-unstyled{margin:0}.highlight,mark{background:#ff6;padding:.1em .2em}.icon-bar{background-color:#888}img{max-width:100%}.popover{width:250px}.sub-breadcrumb{padding:5px 10px;white-space:nowrap}.sub-breadcrumb li{display:inline-block}.sub-breadcrumb li+li:before{padding-left:5px;padding-right:5px;color:#ccc;content:"/\00a0"}.tab-content{padding:4px}@media (max-width:991px){header .container.navbar{margin-bottom:0}.searchForm{margin-top:0}}@media (min-width:768px){h2{font-size:23px;font-weight:normal}h3{font-size:20px;font-weight:normal}.form-control{max-width:400px}}@media (max-width:767px){h2{font-size:20px}h3{font-size:16px}.searchForm{padding-top:0}}.has-error{margin-bottom:0}.sms-error{margin-bottom:0}.sms-error .help-block,.sms-error .control-label,.sms-error .radio,.sms-error .checkbox,.sms-error .radio-inline,.sms-error .checkbox-inline,.sms-error.radio label,.sms-error.checkbox label,.sms-error.radio-inline label,.sms-error.checkbox-inline label{color:#a94442}.sms-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.sms-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.sms-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.sms-error .form-control-feedback{color:#a94442}.help-block.with-errors{margin:0;padding-top:6px;padding-bottom:6px}.help-block.with-errors:empty{padding:0}.badge a{color:#fff}.browse.list-group .list-group-item{word-wrap:break-word}.browse.list-group .list-group-item.view-record{padding:2px 4px;font-size:85%;text-align:right;border-top:0}.cart-controls .checkbox{line-height:2.5em;padding-right:1em}#dateVisColorSettings{background-color:#fff;fill:#eaeaea;outline-color:#c38835;stroke:#265680}.table{table-layout:fixed;word-wrap:break-word}.node{position:absolute;box-sizing:content-box;margin:-1px;overflow:hidden;font:10px sans-serif;line-height:12px;border:1px solid white}.node div{margin-top:0}.toplevel{border:2px solid black}.node .label{position:absolute;bottom:0;left:0;min-height:1px;padding:2px 4px;font-size:85%;background-color:rgba(0,0,0,0.5);border-radius:0;text-shadow:none}.notalabel{color:#000}#viz-instructions{padding-top:600px}span[class^="services-"],span[class*=" services-"] span::before{content:", "}span[class^="services-"],span[class*=" services-"] span:first-of-type::before{content:""} \ No newline at end of file diff --git a/themes/bootstrap3/css/vendor/ol/ol.css b/themes/bootstrap3/css/vendor/ol/ol.css new file mode 100644 index 0000000000000000000000000000000000000000..251dcb463dca4cc989d9e3b50c43ff3c536645f6 --- /dev/null +++ b/themes/bootstrap3/css/vendor/ol/ol.css @@ -0,0 +1 @@ +.ol-control,.ol-scale-line{position:absolute;padding:2px}.ol-box{box-sizing:border-box;border-radius:2px;border:2px solid #00f}.ol-mouse-position{top:8px;right:8px;position:absolute}.ol-scale-line{background:rgba(0,60,136,.3);border-radius:4px;bottom:8px;left:8px}.ol-scale-line-inner{border:1px solid #eee;border-top:none;color:#eee;font-size:10px;text-align:center;margin:1px;will-change:contents,width}.ol-overlay-container{will-change:left,right,top,bottom}.ol-unsupported{display:none}.ol-viewport .ol-unselectable{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:transparent}.ol-control{background-color:rgba(255,255,255,.4);border-radius:4px}.ol-control:hover{background-color:rgba(255,255,255,.6)}.ol-zoom{top:.5em;left:.5em}.ol-rotate{top:.5em;right:.5em;transition:opacity .25s linear,visibility 0s linear}.ol-rotate.ol-hidden{opacity:0;visibility:hidden;transition:opacity .25s linear,visibility 0s linear .25s}.ol-zoom-extent{top:4.643em;left:.5em}.ol-full-screen{right:.5em;top:.5em}@media print{.ol-control{display:none}}.ol-control button{display:block;margin:1px;padding:0;color:#fff;font-size:1.14em;font-weight:700;text-decoration:none;text-align:center;height:1.375em;width:1.375em;line-height:.4em;background-color:rgba(0,60,136,.5);border:none;border-radius:2px}.ol-control button::-moz-focus-inner{border:none;padding:0}.ol-zoom-extent button{line-height:1.4em}.ol-compass{display:block;font-weight:400;font-size:1.2em;will-change:transform}.ol-touch .ol-control button{font-size:1.5em}.ol-touch .ol-zoom-extent{top:5.5em}.ol-control button:focus,.ol-control button:hover{text-decoration:none;background-color:rgba(0,60,136,.7)}.ol-zoom .ol-zoom-in{border-radius:2px 2px 0 0}.ol-zoom .ol-zoom-out{border-radius:0 0 2px 2px}.ol-attribution{text-align:right;bottom:.5em;right:.5em;max-width:calc(100% - 1.3em)}.ol-attribution ul{margin:0;padding:0 .5em;font-size:.7rem;line-height:1.375em;color:#000;text-shadow:0 0 2px #fff}.ol-attribution li{display:inline;list-style:none;line-height:inherit}.ol-attribution li:not(:last-child):after{content:" "}.ol-attribution img{max-height:2em;max-width:inherit;vertical-align:middle}.ol-attribution button,.ol-attribution ul{display:inline-block}.ol-attribution.ol-collapsed ul{display:none}.ol-attribution.ol-logo-only ul{display:block}.ol-attribution:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-attribution.ol-uncollapsible{bottom:0;right:0;border-radius:4px 0 0;height:1.1em;line-height:1em}.ol-attribution.ol-logo-only{background:0 0;bottom:.4em;height:1.1em;line-height:1em}.ol-attribution.ol-uncollapsible img{margin-top:-.2em;max-height:1.6em}.ol-attribution.ol-logo-only button,.ol-attribution.ol-uncollapsible button{display:none}.ol-zoomslider{top:4.5em;left:.5em;height:200px}.ol-zoomslider button{position:relative;height:10px}.ol-touch .ol-zoomslider{top:5.5em}.ol-overviewmap{left:.5em;bottom:.5em}.ol-overviewmap.ol-uncollapsible{bottom:0;left:0;border-radius:0 4px 0 0}.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button{display:inline-block}.ol-overviewmap .ol-overviewmap-map{border:1px solid #7b98bc;height:150px;margin:2px;width:150px}.ol-overviewmap:not(.ol-collapsed) button{bottom:1px;left:2px;position:absolute}.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button{display:none}.ol-overviewmap:not(.ol-collapsed){background:rgba(255,255,255,.8)}.ol-overviewmap-box{border:2px dotted rgba(0,60,136,.7)} diff --git a/themes/bootstrap3/js/map_selection.js b/themes/bootstrap3/js/map_selection.js new file mode 100644 index 0000000000000000000000000000000000000000..b1054e62241b9d7be0ba3c14903493f3479b91b4 --- /dev/null +++ b/themes/bootstrap3/js/map_selection.js @@ -0,0 +1,272 @@ +/*global ol */ +/*exported loadMapSelection */ +//Coordinate order: Storage and Query: WENS ; Display: WSEN + +function loadMapSelection(geoField, boundingBox, baseURL, homeURL, searchParams, showSelection, resultsCoords, popupTitle) { + var init = true; + var pTitle = popupTitle + '<button class="close">×</button>'; + var srcProj = 'EPSG:4326'; + var dstProj = 'EPSG:900913'; + var osm = new ol.layer.Tile({source: new ol.source.OSM()}); + var searchboxSource = new ol.source.Vector(); + var searchboxStyle = new ol.style.Style({ + fill: new ol.style.Fill({ + color: [255, 0, 0, .1] + }), + stroke: new ol.style.Stroke({ + color: [255, 0, 0, 1], + width: 2 + }) + }); + var searchboxLayer = new ol.layer.Vector({ + source: searchboxSource, + style: searchboxStyle + }); + var draw, map; + var count = resultsCoords.length; + var searchResults = new Array(count); + var searchIds = new Array(count); + for (var i = 0; i < count; ++i) { + var coordinates = ol.proj.transform( + [resultsCoords[i][1], resultsCoords[i][2]], srcProj, dstProj + ); + searchResults[i] = new ol.Feature({ + geometry: new ol.geom.Point(coordinates), + id: resultsCoords[i][0], + name: resultsCoords[i][3] + }); + searchIds[i] = resultsCoords[i][0]; + } + var resultSource = new ol.source.Vector({ + features: searchResults + }); + var clusterSource = new ol.source.Cluster({ + distance: 60, + source: resultSource + }); + var styleCache = {}; + var clusterLayer = new ol.layer.Vector({ + id: 'clusterLayer', + source: clusterSource, + style: function addClusterStyle(feature) { + var size = feature.get('features').length; + var pointRadius = 8 + (size.toString().length * 2); + var style = styleCache[size]; + if (!style) { + style = [ + new ol.style.Style({ + image: new ol.style.Circle({ + radius: pointRadius, + stroke: new ol.style.Stroke({ + color: '#ff0000' + }), + fill: new ol.style.Fill({ + color: '#ffb3b3' + }) + }), + text: new ol.style.Text({ + text: size.toString(), + font: 'bold 12px arial,sans-serif', + fill: new ol.style.Fill({ + color: 'black' + }) + }) + }) + ]; + styleCache[size] = style; + } + map.removeInteraction(draw); + return style; + } + }); + + $('#geo_search').show(); + init = function drawMap() { + map = new ol.Map({ + interactions: ol.interaction.defaults({ + shiftDragZoom: false + }), + target: 'geo_search_map', + projection: dstProj, + layers: [osm, searchboxLayer, clusterLayer], + view: new ol.View({ + center: [0, 0], + zoom: 1 + }) + }); + + if (showSelection === true) { + searchboxSource.clear(); + // Adjust bounding box (WSEN) display for queries crossing the dateline + if (boundingBox[0] > boundingBox[2]) { + boundingBox[2] = boundingBox[2] + 360; + } + var newBbox = new ol.geom.Polygon([[ + ol.proj.transform([boundingBox[0], boundingBox[3]], srcProj, dstProj), + ol.proj.transform([boundingBox[0], boundingBox[1]], srcProj, dstProj), + ol.proj.transform([boundingBox[2], boundingBox[1]], srcProj, dstProj), + ol.proj.transform([boundingBox[2], boundingBox[3]], srcProj, dstProj), + ol.proj.transform([boundingBox[0], boundingBox[3]], srcProj, dstProj) + ]]); + var featureBbox = new ol.Feature({ + name: "bbox", + geometry: newBbox + }); + searchboxSource.addFeature(featureBbox); + map.getView().fit(searchboxSource.getExtent(), map.getSize()); + } + + //Get popup elements from webpage + var element = document.getElementById('popup'); + + // Add popup element to map + var popup = new ol.Overlay({ + element: element, + stopEvent: true + }); + map.addOverlay(popup); + + // Display popup on click + map.on('click', function displayPopup(evt) { + var popupfeature = map.forEachFeatureAtPixel(evt.pixel, + function showFeature(feature) { + return {'feature': feature, 'layer': clusterLayer}; + }); + if (popupfeature) { + var cFeatures = popupfeature.feature.get('features'); + var fType = typeof cFeatures; + var fLayerId = popupfeature.layer.get('id'); + if ((fLayerId === 'clusterLayer') && (fType === 'object') && (cFeatures.length < 5)) { + var coordinate = map.getCoordinateFromPixel(evt.pixel); + var pcontent = ''; + for (var j = 0; j < cFeatures.length; j++) { + var cFeatureName = cFeatures[j].get('name'); + var cFeatureId = cFeatures[j].get('id'); + var cFeatureContent = '<article class="geoItem">' + + cFeatureName.link(homeURL + 'Record/' + cFeatureId) + '</article>'; + pcontent += cFeatureContent; + } + popup.setPosition(coordinate); + $(element).popover({ + 'placement': 'auto', + 'container': 'body', + 'animation': false, + 'html': true, + 'title': pTitle + }).on('shown.bs.popover', function closePopup(e) { + // 'aria-describedby' is the id of the current popover + var current_popover = '#' + $(e.target).attr('aria-describedby'); + var $cur_pop = $(current_popover); + $cur_pop.find('.close').click(function closeCurPop(){ + $(element).popover('hide'); + }); + }); + $(element).data('bs.popover').options.content = pcontent; + $(element).popover('show'); + } + } else { + $(element).popover('destroy'); + } + }); + + // change mouse cursor when over marker + map.on('pointermove', function changeMouseCursor(evt) { + if (evt.dragging) { + $(element).popover('destroy'); + return; + } + var pixel = map.getEventPixel(evt.originalEvent); + var hit = map.hasFeatureAtPixel(pixel); + if (hit) { + var fl = map.forEachFeatureAtPixel(pixel, function getFeature(feature) { + return {'feature': feature, 'layer': clusterLayer}; + }); + var cFeatures = fl.feature.get('features'); + var fType = typeof cFeatures; + var fLayerId = fl.layer.get('id'); + if ((fLayerId === 'clusterLayer') && (fType === 'object') && (cFeatures.length < 5)) { + map.getTargetElement().style.cursor = 'pointer'; + } else { + map.getTargetElement().style.cursor = 'default'; + } + } + }); + } + function addInteraction() { + draw = new ol.interaction.Draw ({ + source: searchboxSource, + type: 'LineString', + maxPoints: 2, + geometryFunction: function rectangleFunction(coords, geometryParam) { + var geometry = geometryParam ? geometryParam : new ol.geom.Polygon(null); + var start = coords[0]; + var end = coords[1]; + geometry.setCoordinates([ + [start, [start[0], end[1]], end, [end[0], start[1]], start] + ]); + return geometry; + } + }); + + draw.on('drawend', function drawSearchBox(evt) { + var geometry = evt.feature.getGeometry(); + var geoCoordinates = geometry.getCoordinates(); + var westnorth = ol.proj.transform(geoCoordinates[0][0], dstProj, srcProj); + var eastsouth = ol.proj.transform(geoCoordinates[0][2], dstProj, srcProj); + // Check to make sure the coordinates are in the correct order + var west = westnorth[0]; + var east = eastsouth[0]; + var north = westnorth[1]; + var south = eastsouth[1]; + if (west > east){ + west = eastsouth[0]; + east = westnorth[0]; + } + if (south > north) { + north = eastsouth[1]; + south = westnorth[1]; + } + // Make corrections for queries that cross the dateline + if (west > 180) { + if (west > 360) { + west = west - (360 * Math.floor(west / -360)); + if (west > 180) { + west = west - 360; + } + } else { + west = west - 360; + } + } + if (west < -180) { + if (west < -360) { + west = west + (360 * Math.floor(west / -360)); + if (west < -180) { + west = west + 360; + } + } else { + west = west + 360; + } + } + if (east > 180) { + // Fix overlapping longitudinal query parameters + if (east > 360) { + east = east - (360 * Math.floor(east / 360)); + if (east > 180) { + east = east - 360; + } + } else { + east = east - 360; + } + } + var rawFilter = geoField + ':Intersects(ENVELOPE(' + west + ', ' + east + ', ' + north + ', ' + south + '))'; + location.href = baseURL + searchParams + "&filter[]=" + rawFilter; + }, this); + map.addInteraction(draw); + } + init(); + document.getElementById("draw_box").onclick = function clearAndDrawMap() { + map.removeInteraction(draw); + addInteraction(); + } + init = false; +} diff --git a/themes/bootstrap3/js/map_tab_ol.js b/themes/bootstrap3/js/map_tab_ol.js new file mode 100644 index 0000000000000000000000000000000000000000..9477a38a5aae4f3369a97599ab674638e50c2042 --- /dev/null +++ b/themes/bootstrap3/js/map_tab_ol.js @@ -0,0 +1,173 @@ +/*global ol */ +/*exported loadMapTab */ +//Coordinate order: Storage and Query: WENS ; Display: WSEN +function loadMapTab(mapData, popupTitle) { + var init = true; + var pTitle = popupTitle + '<button class="close">×</button>'; + var srcProj = 'EPSG:4326'; + var dstProj = 'EPSG:900913'; + var osm = new ol.layer.Tile({source: new ol.source.OSM()}); + var vectorSource = new ol.source.Vector(); + var map; + var iconStyle = new ol.style.Style({ + image: new ol.style.Circle({ + radius: 5, + fill: new ol.style.Fill({ + color: 'red' + }) + }) + }); + var polyStyle = new ol.style.Style({ + fill: new ol.style.Fill({ + color: [200, 0, 0, .1] + }), + stroke: new ol.style.Stroke({ + color: 'red', + width: 2 + }) + }); + + $('#map-canvas').show(); + init = function drawMap() { + var featureCount = mapData.length; + var label, label_on; + var label_name; + var label_coords, label_coord, label_coord1, label_coord2; + var i = 0; + for (i; i < featureCount; i++) { + // Construct the label names + label_name = mapData[i][5]; + //Construct the coordinate labels + label_coords = mapData[i][6]; + if (label_coords) { + label_coord1 = mapData[i][6].substring(0, 16); + label_coord2 = mapData[i][6].substring(16); + if (label_coord2) { + label_coord = label_coord1 + '<br/>' + label_coord2; + } else { + label_coord = label_coord1; + } + } + // Construct the entire label string + if (label_coord && label_name) { + label = label_coord + '<br/>' + label_name; + label_on = true; + } else if (label_name){ + label = label_name; + label_on = true; + } else if (label_coord) { + label = label_coord; + label_on = true; + } else { + label = 'No information available'; + label_on = false; + } + // Determine if entry is point or polygon - Does W=E & N=S? // + if (mapData[i][4] === 2) { + //It's a point feature // + var lonlat = ol.proj.transform([mapData[i][0], mapData[i][1]], srcProj, dstProj); + var iconFeature = new ol.Feature({ + geometry: new ol.geom.Point(lonlat), + name: label + }); + iconFeature.setStyle(iconStyle); + vectorSource.addFeature(iconFeature); + } else if (mapData[i][4] === 4) { // It's a polygon feature // + var point1 = ol.proj.transform([mapData[i][0], mapData[i][3]], srcProj, dstProj); + var point2 = ol.proj.transform([mapData[i][0], mapData[i][1]], srcProj, dstProj); + var point3 = ol.proj.transform([mapData[i][2], mapData[i][1]], srcProj, dstProj); + var point4 = ol.proj.transform([mapData[i][2], mapData[i][3]], srcProj, dstProj); + var polyFeature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [point1, point2, point3, point4, point1] + ]), + name: label + }); + polyFeature.setStyle(polyStyle); + vectorSource.addFeature(polyFeature); + } + } + var vectorLayer = new ol.layer.Vector({ + source: vectorSource, + renderBuffer: 500 + }); + map = new ol.Map({ + renderer: 'canvas', + projection: dstProj, + layers: [osm, vectorLayer], + target: 'map-canvas', + view: new ol.View({ + center: [0, 0], + zoom: 1 + }) + }); + + // Adjust zoom extent and center of map + if (featureCount === 1 && mapData[0][4] === 2) { + map.getView().setZoom(4); + var centerCoord = ol.proj.transform([mapData[0][0], mapData[0][1]], srcProj, dstProj); + map.getView().setCenter(centerCoord); + } else { + var extent = vectorLayer.getSource().getExtent(); + map.getView().fit(extent, map.getSize()); + } + + // Turn on popup tool tips if labels or coordinates are enabled. + if (label_on === true) { + var element = document.getElementById('popup'); + var popup = new ol.Overlay({ + element: element, + stopEvent: true + }); + map.addOverlay(popup); + + // Display popup on click + map.on('click', function displayPopup(evt) { + var popupfeature = map.forEachFeatureAtPixel(evt.pixel, + function showFeature(feature) { + return feature; + }); + if (popupfeature) { + var coordinate = evt.coordinate; + popup.setPosition(coordinate); + $(element).popover({ + 'placement': 'auto', + 'container': 'body', + 'animation': false, + 'html': true, + 'title': pTitle + }).on('shown.bs.popover', function closePopup(e) { + // 'aria-describedby' is the id of the current popover + var current_popover = '#' + $(e.target).attr('aria-describedby'); + var $cur_pop = $(current_popover); + $cur_pop.find('.close').click(function closeCurPop(){ + $(element).popover('destroy'); + }); + }); + $(element).data('bs.popover').options.content = popupfeature.get('name'); + $(element).popover('show'); + } else { + $(element).popover('destroy'); + } + }); + + // change mouse cursor when over marker + map.on('pointermove', function changeMouseCursor(e) { + if (e.dragging) { + $(element).popover('destroy'); + return; + } + var pixel = map.getEventPixel(e.originalEvent); + var hit = map.hasFeatureAtPixel(pixel); + var target = map.getTarget(); + if (hit === true) { + document.getElementById(target).style.cursor = "pointer"; + } else { + document.getElementById(target).style.cursor = "default"; + } + }); + } + } + init(); + init = false; +} diff --git a/themes/bootstrap3/js/vendor/ol/ol-debug.js b/themes/bootstrap3/js/vendor/ol/ol-debug.js new file mode 100644 index 0000000000000000000000000000000000000000..56fd2d897aaa3dee3d10d350a4a684e2987963e6 --- /dev/null +++ b/themes/bootstrap3/js/vendor/ol/ol-debug.js @@ -0,0 +1,107541 @@ +// OpenLayers 3. See http://openlayers.org/ +// License: https://raw.githubusercontent.com/openlayers/ol3/master/LICENSE.md +// Version: v3.17.1 + +(function (root, factory) { + if (typeof exports === "object") { + module.exports = factory(); + } else if (typeof define === "function" && define.amd) { + define([], factory); + } else { + root.ol = factory(); + } +}(this, function () { + var OPENLAYERS = {}; + var goog = this.goog = {}; +this.CLOSURE_NO_DEPS = true; +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Bootstrap for the Google JS Library (Closure). + * + * In uncompiled mode base.js will write out Closure's deps file, unless the + * global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects to + * include their own deps file(s) from different locations. + * + * @author arv@google.com (Erik Arvidsson) + * + * @provideGoog + */ + + +/** + * @define {boolean} Overridden to true by the compiler when + * --process_closure_primitives is specified. + */ +var COMPILED = false; + + +/** + * Base namespace for the Closure library. Checks to see goog is already + * defined in the current scope before assigning to prevent clobbering if + * base.js is loaded more than once. + * + * @const + */ +var goog = goog || {}; + + +/** + * Reference to the global context. In most cases this will be 'window'. + */ +goog.global = this; + + +/** + * A hook for overriding the define values in uncompiled mode. + * + * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before + * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES}, + * {@code goog.define} will use the value instead of the default value. This + * allows flags to be overwritten without compilation (this is normally + * accomplished with the compiler's "define" flag). + * + * Example: + * <pre> + * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false}; + * </pre> + * + * @type {Object<string, (string|number|boolean)>|undefined} + */ +goog.global.CLOSURE_UNCOMPILED_DEFINES; + + +/** + * A hook for overriding the define values in uncompiled or compiled mode, + * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In + * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. + * + * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or + * string literals or the compiler will emit an error. + * + * While any @define value may be set, only those set with goog.define will be + * effective for uncompiled code. + * + * Example: + * <pre> + * var CLOSURE_DEFINES = {'goog.DEBUG': false} ; + * </pre> + * + * @type {Object<string, (string|number|boolean)>|undefined} + */ +goog.global.CLOSURE_DEFINES; + + +/** + * Returns true if the specified value is not undefined. + * WARNING: Do not use this to test if an object has a property. Use the in + * operator instead. + * + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined. + */ +goog.isDef = function(val) { + // void 0 always evaluates to undefined and hence we do not need to depend on + // the definition of the global variable named 'undefined'. + return val !== void 0; +}; + + +/** + * Builds an object structure for the provided namespace path, ensuring that + * names that already exist are not overwritten. For example: + * "a.b.c" -> a = {};a.b={};a.b.c={}; + * Used by goog.provide and goog.exportSymbol. + * @param {string} name name of the object that this file defines. + * @param {*=} opt_object the object to expose at the end of the path. + * @param {Object=} opt_objectToExportTo The object to add the path to; default + * is |goog.global|. + * @private + */ +goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { + var parts = name.split('.'); + var cur = opt_objectToExportTo || goog.global; + + // Internet Explorer exhibits strange behavior when throwing errors from + // methods externed in this manner. See the testExportSymbolExceptions in + // base_test.html for an example. + if (!(parts[0] in cur) && cur.execScript) { + cur.execScript('var ' + parts[0]); + } + + // Certain browsers cannot parse code in the form for((a in b); c;); + // This pattern is produced by the JSCompiler when it collapses the + // statement above into the conditional loop below. To prevent this from + // happening, use a for-loop and reserve the init logic as below. + + // Parentheses added to eliminate strict JS warning in Firefox. + for (var part; parts.length && (part = parts.shift());) { + if (!parts.length && goog.isDef(opt_object)) { + // last part and we have an object; use it + cur[part] = opt_object; + } else if (cur[part]) { + cur = cur[part]; + } else { + cur = cur[part] = {}; + } + } +}; + + +/** + * Defines a named value. In uncompiled mode, the value is retrieved from + * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and + * has the property specified, and otherwise used the defined defaultValue. + * When compiled the default can be overridden using the compiler + * options or the value set in the CLOSURE_DEFINES object. + * + * @param {string} name The distinguished name to provide. + * @param {string|number|boolean} defaultValue + */ +goog.define = function(name, defaultValue) { + var value = defaultValue; + if (!COMPILED) { + if (goog.global.CLOSURE_UNCOMPILED_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { + value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; + } else if ( + goog.global.CLOSURE_DEFINES && + Object.prototype.hasOwnProperty.call( + goog.global.CLOSURE_DEFINES, name)) { + value = goog.global.CLOSURE_DEFINES[name]; + } + } + goog.exportPath_(name, value); +}; + + +/** + * @define {boolean} DEBUG is provided as a convenience so that debugging code + * that should not be included in a production js_binary can be easily stripped + * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most + * toString() methods should be declared inside an "if (goog.DEBUG)" conditional + * because they are generally used for debugging purposes and it is difficult + * for the JSCompiler to statically determine whether they are used. + */ +goog.define('goog.DEBUG', true); + + +/** + * @define {string} LOCALE defines the locale being used for compilation. It is + * used to select locale specific data to be compiled in js binary. BUILD rule + * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler + * option. + * + * Take into account that the locale code format is important. You should use + * the canonical Unicode format with hyphen as a delimiter. Language must be + * lowercase, Language Script - Capitalized, Region - UPPERCASE. + * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. + * + * See more info about locale codes here: + * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers + * + * For language codes you should use values defined by ISO 693-1. See it here + * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from + * this rule: the Hebrew language. For legacy reasons the old code (iw) should + * be used instead of the new code (he), see http://wiki/Main/IIISynonyms. + */ +goog.define('goog.LOCALE', 'en'); // default to en + + +/** + * @define {boolean} Whether this code is running on trusted sites. + * + * On untrusted sites, several native functions can be defined or overridden by + * external libraries like Prototype, Datejs, and JQuery and setting this flag + * to false forces closure to use its own implementations when possible. + * + * If your JavaScript can be loaded by a third party site and you are wary about + * relying on non-standard implementations, specify + * "--define goog.TRUSTED_SITE=false" to the JSCompiler. + */ +goog.define('goog.TRUSTED_SITE', true); + + +/** + * @define {boolean} Whether a project is expected to be running in strict mode. + * + * This define can be used to trigger alternate implementations compatible with + * running in EcmaScript Strict mode or warn about unavailable functionality. + * @see https://goo.gl/PudQ4y + * + */ +goog.define('goog.STRICT_MODE_COMPATIBLE', false); + + +/** + * @define {boolean} Whether code that calls {@link goog.setTestOnly} should + * be disallowed in the compilation unit. + */ +goog.define('goog.DISALLOW_TEST_ONLY_CODE', COMPILED && !goog.DEBUG); + + +/** + * @define {boolean} Whether to use a Chrome app CSP-compliant method for + * loading scripts via goog.require. @see appendScriptSrcNode_. + */ +goog.define('goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING', false); + + +/** + * Defines a namespace in Closure. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * The presence of one or more goog.provide() calls in a file indicates + * that the file defines the given objects/namespaces. + * Provided symbols must not be null or undefined. + * + * In addition, goog.provide() creates the object stubs for a namespace + * (for example, goog.provide("goog.foo.bar") will create the object + * goog.foo.bar if it does not already exist). + * + * Build tools also scan for provide/require/module statements + * to discern dependencies, build dependency files (see deps.js), etc. + * + * @see goog.require + * @see goog.module + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + */ +goog.provide = function(name) { + if (goog.isInModuleLoader_()) { + throw Error('goog.provide can not be used within a goog.module.'); + } + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + } + + goog.constructNamespace_(name); +}; + + +/** + * @param {string} name Namespace provided by this file in the form + * "goog.package.part". + * @param {Object=} opt_obj The object to embed in the namespace. + * @private + */ +goog.constructNamespace_ = function(name, opt_obj) { + if (!COMPILED) { + delete goog.implicitNamespaces_[name]; + + var namespace = name; + while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { + if (goog.getObjectByName(namespace)) { + break; + } + goog.implicitNamespaces_[namespace] = true; + } + } + + goog.exportPath_(name, opt_obj); +}; + + +/** + * Module identifier validation regexp. + * Note: This is a conservative check, it is very possible to be more lenient, + * the primary exclusion here is "/" and "\" and a leading ".", these + * restrictions are intended to leave the door open for using goog.require + * with relative file paths rather than module identifiers. + * @private + */ +goog.VALID_MODULE_RE_ = /^[a-zA-Z_$][a-zA-Z0-9._$]*$/; + + +/** + * Defines a module in Closure. + * + * Marks that this file must be loaded as a module and claims the namespace. + * + * A namespace may only be defined once in a codebase. It may be defined using + * goog.provide() or goog.module(). + * + * goog.module() has three requirements: + * - goog.module may not be used in the same file as goog.provide. + * - goog.module must be the first statement in the file. + * - only one goog.module is allowed per file. + * + * When a goog.module annotated file is loaded, it is enclosed in + * a strict function closure. This means that: + * - any variables declared in a goog.module file are private to the file + * (not global), though the compiler is expected to inline the module. + * - The code must obey all the rules of "strict" JavaScript. + * - the file will be marked as "use strict" + * + * NOTE: unlike goog.provide, goog.module does not declare any symbols by + * itself. If declared symbols are desired, use + * goog.module.declareLegacyNamespace(). + * + * + * See the public goog.module proposal: http://goo.gl/Va1hin + * + * @param {string} name Namespace provided by this file in the form + * "goog.package.part", is expected but not required. + */ +goog.module = function(name) { + if (!goog.isString(name) || !name || + name.search(goog.VALID_MODULE_RE_) == -1) { + throw Error('Invalid module identifier'); + } + if (!goog.isInModuleLoader_()) { + throw Error('Module ' + name + ' has been loaded incorrectly.'); + } + if (goog.moduleLoaderState_.moduleName) { + throw Error('goog.module may only be called once per module.'); + } + + // Store the module name for the loader. + goog.moduleLoaderState_.moduleName = name; + if (!COMPILED) { + // Ensure that the same namespace isn't provided twice. + // A goog.module/goog.provide maps a goog.require to a specific file + if (goog.isProvided_(name)) { + throw Error('Namespace "' + name + '" already declared.'); + } + delete goog.implicitNamespaces_[name]; + } +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * + * Note: This is not an alternative to goog.require, it does not + * indicate a hard dependency, instead it is used to indicate + * an optional dependency or to access the exports of a module + * that has already been loaded. + * @suppress {missingProvide} + */ +goog.module.get = function(name) { + return goog.module.getInternal_(name); +}; + + +/** + * @param {string} name The module identifier. + * @return {?} The module exports for an already loaded module or null. + * @private + */ +goog.module.getInternal_ = function(name) { + if (!COMPILED) { + if (goog.isProvided_(name)) { + // goog.require only return a value with-in goog.module files. + return name in goog.loadedModules_ ? goog.loadedModules_[name] : + goog.getObjectByName(name); + } else { + return null; + } + } +}; + + +/** + * @private {?{moduleName: (string|undefined), declareLegacyNamespace:boolean}} + */ +goog.moduleLoaderState_ = null; + + +/** + * @private + * @return {boolean} Whether a goog.module is currently being initialized. + */ +goog.isInModuleLoader_ = function() { + return goog.moduleLoaderState_ != null; +}; + + +/** + * Provide the module's exports as a globally accessible object under the + * module's declared name. This is intended to ease migration to goog.module + * for files that have existing usages. + * @suppress {missingProvide} + */ +goog.module.declareLegacyNamespace = function() { + if (!COMPILED && !goog.isInModuleLoader_()) { + throw new Error( + 'goog.module.declareLegacyNamespace must be called from ' + + 'within a goog.module'); + } + if (!COMPILED && !goog.moduleLoaderState_.moduleName) { + throw Error( + 'goog.module must be called prior to ' + + 'goog.module.declareLegacyNamespace.'); + } + goog.moduleLoaderState_.declareLegacyNamespace = true; +}; + + +/** + * Marks that the current file should only be used for testing, and never for + * live code in production. + * + * In the case of unit tests, the message may optionally be an exact namespace + * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra + * provide (if not explicitly defined in the code). + * + * @param {string=} opt_message Optional message to add to the error that's + * raised when used in production code. + */ +goog.setTestOnly = function(opt_message) { + if (goog.DISALLOW_TEST_ONLY_CODE) { + opt_message = opt_message || ''; + throw Error( + 'Importing test-only code into non-debug environment' + + (opt_message ? ': ' + opt_message : '.')); + } +}; + + +/** + * Forward declares a symbol. This is an indication to the compiler that the + * symbol may be used in the source yet is not required and may not be provided + * in compilation. + * + * The most common usage of forward declaration is code that takes a type as a + * function parameter but does not need to require it. By forward declaring + * instead of requiring, no hard dependency is made, and (if not required + * elsewhere) the namespace may never be required and thus, not be pulled + * into the JavaScript binary. If it is required elsewhere, it will be type + * checked as normal. + * + * + * @param {string} name The namespace to forward declare in the form of + * "goog.package.part". + */ +goog.forwardDeclare = function(name) {}; + + +/** + * Forward declare type information. Used to assign types to goog.global + * referenced object that would otherwise result in unknown type references + * and thus block property disambiguation. + */ +goog.forwardDeclare('Document'); +goog.forwardDeclare('HTMLScriptElement'); +goog.forwardDeclare('XMLHttpRequest'); + + +if (!COMPILED) { + /** + * Check if the given name has been goog.provided. This will return false for + * names that are available only as implicit namespaces. + * @param {string} name name of the object to look for. + * @return {boolean} Whether the name has been provided. + * @private + */ + goog.isProvided_ = function(name) { + return (name in goog.loadedModules_) || + (!goog.implicitNamespaces_[name] && + goog.isDefAndNotNull(goog.getObjectByName(name))); + }; + + /** + * Namespaces implicitly defined by goog.provide. For example, + * goog.provide('goog.events.Event') implicitly declares that 'goog' and + * 'goog.events' must be namespaces. + * + * @type {!Object<string, (boolean|undefined)>} + * @private + */ + goog.implicitNamespaces_ = {'goog.module': true}; + + // NOTE: We add goog.module as an implicit namespace as goog.module is defined + // here and because the existing module package has not been moved yet out of + // the goog.module namespace. This satisifies both the debug loader and + // ahead-of-time dependency management. +} + + +/** + * Returns an object based on its fully qualified external name. The object + * is not found if null or undefined. If you are using a compilation pass that + * renames property names beware that using this function will not find renamed + * properties. + * + * @param {string} name The fully qualified name. + * @param {Object=} opt_obj The object within which to look; default is + * |goog.global|. + * @return {?} The value (object or primitive) or, if not found, null. + */ +goog.getObjectByName = function(name, opt_obj) { + var parts = name.split('.'); + var cur = opt_obj || goog.global; + for (var part; part = parts.shift();) { + if (goog.isDefAndNotNull(cur[part])) { + cur = cur[part]; + } else { + return null; + } + } + return cur; +}; + + +/** + * Globalizes a whole namespace, such as goog or goog.lang. + * + * @param {!Object} obj The namespace to globalize. + * @param {Object=} opt_global The object to add the properties to. + * @deprecated Properties may be explicitly exported to the global scope, but + * this should no longer be done in bulk. + */ +goog.globalize = function(obj, opt_global) { + var global = opt_global || goog.global; + for (var x in obj) { + global[x] = obj[x]; + } +}; + + +/** + * Adds a dependency from a file to the files it requires. + * @param {string} relPath The path to the js file. + * @param {!Array<string>} provides An array of strings with + * the names of the objects this file provides. + * @param {!Array<string>} requires An array of strings with + * the names of the objects this file requires. + * @param {boolean|!Object<string>=} opt_loadFlags Parameters indicating + * how the file must be loaded. The boolean 'true' is equivalent + * to {'module': 'goog'} for backwards-compatibility. Valid properties + * and values include {'module': 'goog'} and {'lang': 'es6'}. + */ +goog.addDependency = function(relPath, provides, requires, opt_loadFlags) { + if (goog.DEPENDENCIES_ENABLED) { + var provide, require; + var path = relPath.replace(/\\/g, '/'); + var deps = goog.dependencies_; + if (!opt_loadFlags || typeof opt_loadFlags === 'boolean') { + opt_loadFlags = opt_loadFlags ? {'module': 'goog'} : {}; + } + for (var i = 0; provide = provides[i]; i++) { + deps.nameToPath[provide] = path; + deps.loadFlags[path] = opt_loadFlags; + } + for (var j = 0; require = requires[j]; j++) { + if (!(path in deps.requires)) { + deps.requires[path] = {}; + } + deps.requires[path][require] = true; + } + } +}; + + + + +// NOTE(nnaze): The debug DOM loader was included in base.js as an original way +// to do "debug-mode" development. The dependency system can sometimes be +// confusing, as can the debug DOM loader's asynchronous nature. +// +// With the DOM loader, a call to goog.require() is not blocking -- the script +// will not load until some point after the current script. If a namespace is +// needed at runtime, it needs to be defined in a previous script, or loaded via +// require() with its registered dependencies. +// +// User-defined namespaces may need their own deps file. For a reference on +// creating a deps file, see: +// Externally: https://developers.google.com/closure/library/docs/depswriter +// +// Because of legacy clients, the DOM loader can't be easily removed from +// base.js. Work is being done to make it disableable or replaceable for +// different environments (DOM-less JavaScript interpreters like Rhino or V8, +// for example). See bootstrap/ for more information. + + +/** + * @define {boolean} Whether to enable the debug loader. + * + * If enabled, a call to goog.require() will attempt to load the namespace by + * appending a script tag to the DOM (if the namespace has been registered). + * + * If disabled, goog.require() will simply assert that the namespace has been + * provided (and depend on the fact that some outside tool correctly ordered + * the script). + */ +goog.define('goog.ENABLE_DEBUG_LOADER', true); + + +/** + * @param {string} msg + * @private + */ +goog.logToConsole_ = function(msg) { + if (goog.global.console) { + goog.global.console['error'](msg); + } +}; + + +/** + * Implements a system for the dynamic resolution of dependencies that works in + * parallel with the BUILD system. Note that all calls to goog.require will be + * stripped by the JSCompiler when the --process_closure_primitives option is + * used. + * @see goog.provide + * @param {string} name Namespace to include (as was given in goog.provide()) in + * the form "goog.package.part". + * @return {?} If called within a goog.module file, the associated namespace or + * module otherwise null. + */ +goog.require = function(name) { + // If the object already exists we do not need do do anything. + if (!COMPILED) { + if (goog.ENABLE_DEBUG_LOADER && goog.IS_OLD_IE_) { + goog.maybeProcessDeferredDep_(name); + } + + if (goog.isProvided_(name)) { + if (goog.isInModuleLoader_()) { + return goog.module.getInternal_(name); + } else { + return null; + } + } + + if (goog.ENABLE_DEBUG_LOADER) { + var path = goog.getPathFromDeps_(name); + if (path) { + goog.writeScripts_(path); + return null; + } + } + + var errorMessage = 'goog.require could not find: ' + name; + goog.logToConsole_(errorMessage); + + throw Error(errorMessage); + } +}; + + +/** + * Path for included scripts. + * @type {string} + */ +goog.basePath = ''; + + +/** + * A hook for overriding the base path. + * @type {string|undefined} + */ +goog.global.CLOSURE_BASE_PATH; + + +/** + * Whether to write out Closure's deps file. By default, the deps are written. + * @type {boolean|undefined} + */ +goog.global.CLOSURE_NO_DEPS; + + +/** + * A function to import a single script. This is meant to be overridden when + * Closure is being run in non-HTML contexts, such as web workers. It's defined + * in the global scope so that it can be set before base.js is loaded, which + * allows deps.js to be imported properly. + * + * The function is passed the script source, which is a relative URI. It should + * return true if the script was imported, false otherwise. + * @type {(function(string): boolean)|undefined} + */ +goog.global.CLOSURE_IMPORT_SCRIPT; + + +/** + * Null function used for default values of callbacks, etc. + * @return {void} Nothing. + */ +goog.nullFunction = function() {}; + + +/** + * When defining a class Foo with an abstract method bar(), you can do: + * Foo.prototype.bar = goog.abstractMethod + * + * Now if a subclass of Foo fails to override bar(), an error will be thrown + * when bar() is invoked. + * + * Note: This does not take the name of the function to override as an argument + * because that would make it more difficult to obfuscate our JavaScript code. + * + * @type {!Function} + * @throws {Error} when invoked to indicate the method should be overridden. + */ +goog.abstractMethod = function() { + throw Error('unimplemented abstract method'); +}; + + +/** + * Adds a {@code getInstance} static method that always returns the same + * instance object. + * @param {!Function} ctor The constructor for the class to add the static + * method to. + */ +goog.addSingletonGetter = function(ctor) { + ctor.getInstance = function() { + if (ctor.instance_) { + return ctor.instance_; + } + if (goog.DEBUG) { + // NOTE: JSCompiler can't optimize away Array#push. + goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; + } + return ctor.instance_ = new ctor; + }; +}; + + +/** + * All singleton classes that have been instantiated, for testing. Don't read + * it directly, use the {@code goog.testing.singleton} module. The compiler + * removes this variable if unused. + * @type {!Array<!Function>} + * @private + */ +goog.instantiatedSingletons_ = []; + + +/** + * @define {boolean} Whether to load goog.modules using {@code eval} when using + * the debug loader. This provides a better debugging experience as the + * source is unmodified and can be edited using Chrome Workspaces or similar. + * However in some environments the use of {@code eval} is banned + * so we provide an alternative. + */ +goog.define('goog.LOAD_MODULE_USING_EVAL', true); + + +/** + * @define {boolean} Whether the exports of goog.modules should be sealed when + * possible. + */ +goog.define('goog.SEAL_MODULE_EXPORTS', goog.DEBUG); + + +/** + * The registry of initialized modules: + * the module identifier to module exports map. + * @private @const {!Object<string, ?>} + */ +goog.loadedModules_ = {}; + + +/** + * True if goog.dependencies_ is available. + * @const {boolean} + */ +goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; + + +/** + * @define {string} How to decide whether to transpile. Valid values + * are 'always', 'never', and 'detect'. The default ('detect') is to + * use feature detection to determine which language levels need + * transpilation. + */ +// NOTE(user): we could expand this to accept a language level to bypass +// detection: e.g. goog.TRANSPILE == 'es5' would transpile ES6 files but +// would leave ES3 and ES5 files alone. +goog.define('goog.TRANSPILE', 'detect'); + + +/** + * @define {string} Path to the transpiler. Executing the script at this + * path (relative to base.js) should define a function $jscomp.transpile. + */ +goog.define('goog.TRANSPILER', 'transpile.js'); + + +if (goog.DEPENDENCIES_ENABLED) { + /** + * This object is used to keep track of dependencies and other data that is + * used for loading scripts. + * @private + * @type {{ + * loadFlags: !Object<string, !Object<string, string>>, + * nameToPath: !Object<string, string>, + * requires: !Object<string, !Object<string, boolean>>, + * visited: !Object<string, boolean>, + * written: !Object<string, boolean>, + * deferred: !Object<string, string> + * }} + */ + goog.dependencies_ = { + loadFlags: {}, // 1 to 1 + + nameToPath: {}, // 1 to 1 + + requires: {}, // 1 to many + + // Used when resolving dependencies to prevent us from visiting file twice. + visited: {}, + + written: {}, // Used to keep track of script files we have written. + + deferred: {} // Used to track deferred module evaluations in old IEs + }; + + + /** + * Tries to detect whether is in the context of an HTML document. + * @return {boolean} True if it looks like HTML document. + * @private + */ + goog.inHtmlDocument_ = function() { + /** @type {Document} */ + var doc = goog.global.document; + return doc != null && 'write' in doc; // XULDocument misses write. + }; + + + /** + * Tries to detect the base path of base.js script that bootstraps Closure. + * @private + */ + goog.findBasePath_ = function() { + if (goog.isDef(goog.global.CLOSURE_BASE_PATH)) { + goog.basePath = goog.global.CLOSURE_BASE_PATH; + return; + } else if (!goog.inHtmlDocument_()) { + return; + } + /** @type {Document} */ + var doc = goog.global.document; + var scripts = doc.getElementsByTagName('SCRIPT'); + // Search backwards since the current script is in almost all cases the one + // that has base.js. + for (var i = scripts.length - 1; i >= 0; --i) { + var script = /** @type {!HTMLScriptElement} */ (scripts[i]); + var src = script.src; + var qmark = src.lastIndexOf('?'); + var l = qmark == -1 ? src.length : qmark; + if (src.substr(l - 7, 7) == 'base.js') { + goog.basePath = src.substr(0, l - 7); + return; + } + } + }; + + + /** + * Imports a script if, and only if, that script hasn't already been imported. + * (Must be called at execution time) + * @param {string} src Script source. + * @param {string=} opt_sourceText The optionally source text to evaluate + * @private + */ + goog.importScript_ = function(src, opt_sourceText) { + var importScript = + goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_; + if (importScript(src, opt_sourceText)) { + goog.dependencies_.written[src] = true; + } + }; + + + /** + * Whether the browser is IE9 or earlier, which needs special handling + * for deferred modules. + * @const @private {boolean} + */ + goog.IS_OLD_IE_ = + !!(!goog.global.atob && goog.global.document && goog.global.document.all); + + + /** + * Given a URL initiate retrieval and execution of a script that needs + * pre-processing. + * @param {string} src Script source URL. + * @param {boolean} isModule Whether this is a goog.module. + * @param {boolean} needsTranspile Whether this source needs transpilation. + * @private + */ + goog.importProcessedScript_ = function(src, isModule, needsTranspile) { + // In an attempt to keep browsers from timing out loading scripts using + // synchronous XHRs, put each load in its own script block. + var bootstrap = 'goog.retrieveAndExec_("' + src + '", ' + isModule + ', ' + + needsTranspile + ');'; + + goog.importScript_('', bootstrap); + }; + + + /** @private {!Array<string>} */ + goog.queuedModules_ = []; + + + /** + * Return an appropriate module text. Suitable to insert into + * a script tag (that is unescaped). + * @param {string} srcUrl + * @param {string} scriptText + * @return {string} + * @private + */ + goog.wrapModule_ = function(srcUrl, scriptText) { + if (!goog.LOAD_MODULE_USING_EVAL || !goog.isDef(goog.global.JSON)) { + return '' + + 'goog.loadModule(function(exports) {' + + '"use strict";' + scriptText + + '\n' + // terminate any trailing single line comment. + ';return exports' + + '});' + + '\n//# sourceURL=' + srcUrl + '\n'; + } else { + return '' + + 'goog.loadModule(' + + goog.global.JSON.stringify( + scriptText + '\n//# sourceURL=' + srcUrl + '\n') + + ');'; + } + }; + + // On IE9 and earlier, it is necessary to handle + // deferred module loads. In later browsers, the + // code to be evaluated is simply inserted as a script + // block in the correct order. To eval deferred + // code at the right time, we piggy back on goog.require to call + // goog.maybeProcessDeferredDep_. + // + // The goog.requires are used both to bootstrap + // the loading process (when no deps are available) and + // declare that they should be available. + // + // Here we eval the sources, if all the deps are available + // either already eval'd or goog.require'd. This will + // be the case when all the dependencies have already + // been loaded, and the dependent module is loaded. + // + // But this alone isn't sufficient because it is also + // necessary to handle the case where there is no root + // that is not deferred. For that there we register for an event + // and trigger goog.loadQueuedModules_ handle any remaining deferred + // evaluations. + + /** + * Handle any remaining deferred goog.module evals. + * @private + */ + goog.loadQueuedModules_ = function() { + var count = goog.queuedModules_.length; + if (count > 0) { + var queue = goog.queuedModules_; + goog.queuedModules_ = []; + for (var i = 0; i < count; i++) { + var path = queue[i]; + goog.maybeProcessDeferredPath_(path); + } + } + }; + + + /** + * Eval the named module if its dependencies are + * available. + * @param {string} name The module to load. + * @private + */ + goog.maybeProcessDeferredDep_ = function(name) { + if (goog.isDeferredModule_(name) && goog.allDepsAreAvailable_(name)) { + var path = goog.getPathFromDeps_(name); + goog.maybeProcessDeferredPath_(goog.basePath + path); + } + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose evaluation has been deferred. + * @private + */ + goog.isDeferredModule_ = function(name) { + var path = goog.getPathFromDeps_(name); + var loadFlags = path && goog.dependencies_.loadFlags[path] || {}; + if (path && (loadFlags['module'] == 'goog' || + goog.needsTranspile_(loadFlags['lang']))) { + var abspath = goog.basePath + path; + return (abspath) in goog.dependencies_.deferred; + } + return false; + }; + + /** + * @param {string} name The module to check. + * @return {boolean} Whether the name represents a + * module whose declared dependencies have all been loaded + * (eval'd or a deferred module load) + * @private + */ + goog.allDepsAreAvailable_ = function(name) { + var path = goog.getPathFromDeps_(name); + if (path && (path in goog.dependencies_.requires)) { + for (var requireName in goog.dependencies_.requires[path]) { + if (!goog.isProvided_(requireName) && + !goog.isDeferredModule_(requireName)) { + return false; + } + } + } + return true; + }; + + + /** + * @param {string} abspath + * @private + */ + goog.maybeProcessDeferredPath_ = function(abspath) { + if (abspath in goog.dependencies_.deferred) { + var src = goog.dependencies_.deferred[abspath]; + delete goog.dependencies_.deferred[abspath]; + goog.globalEval(src); + } + }; + + + /** + * Load a goog.module from the provided URL. This is not a general purpose + * code loader and does not support late loading code, that is it should only + * be used during page load. This method exists to support unit tests and + * "debug" loaders that would otherwise have inserted script tags. Under the + * hood this needs to use a synchronous XHR and is not recommeneded for + * production code. + * + * The module's goog.requires must have already been satisified; an exception + * will be thrown if this is not the case. This assumption is that no + * "deps.js" file exists, so there is no way to discover and locate the + * module-to-be-loaded's dependencies and no attempt is made to do so. + * + * There should only be one attempt to load a module. If + * "goog.loadModuleFromUrl" is called for an already loaded module, an + * exception will be throw. + * + * @param {string} url The URL from which to attempt to load the goog.module. + */ + goog.loadModuleFromUrl = function(url) { + // Because this executes synchronously, we don't need to do any additional + // bookkeeping. When "goog.loadModule" the namespace will be marked as + // having been provided which is sufficient. + goog.retrieveAndExec_(url, true, false); + }; + + + /** + * Writes a new script pointing to {@code src} directly into the DOM. + * + * NOTE: This method is not CSP-compliant. @see goog.appendScriptSrcNode_ for + * the fallback mechanism. + * + * @param {string} src The script URL. + * @private + */ + goog.writeScriptSrcNode_ = function(src) { + goog.global.document.write( + '<script type="text/javascript" src="' + src + '"></' + + 'script>'); + }; + + + /** + * Appends a new script node to the DOM using a CSP-compliant mechanism. This + * method exists as a fallback for document.write (which is not allowed in a + * strict CSP context, e.g., Chrome apps). + * + * NOTE: This method is not analogous to using document.write to insert a + * <script> tag; specifically, the user agent will execute a script added by + * document.write immediately after the current script block finishes + * executing, whereas the DOM-appended script node will not be executed until + * the entire document is parsed and executed. That is to say, this script is + * added to the end of the script execution queue. + * + * The page must not attempt to call goog.required entities until after the + * document has loaded, e.g., in or after the window.onload callback. + * + * @param {string} src The script URL. + * @private + */ + goog.appendScriptSrcNode_ = function(src) { + /** @type {Document} */ + var doc = goog.global.document; + var scriptEl = + /** @type {HTMLScriptElement} */ (doc.createElement('script')); + scriptEl.type = 'text/javascript'; + scriptEl.src = src; + scriptEl.defer = false; + scriptEl.async = false; + doc.head.appendChild(scriptEl); + }; + + + /** + * The default implementation of the import function. Writes a script tag to + * import the script. + * + * @param {string} src The script url. + * @param {string=} opt_sourceText The optionally source text to evaluate + * @return {boolean} True if the script was imported, false otherwise. + * @private + */ + goog.writeScriptTag_ = function(src, opt_sourceText) { + if (goog.inHtmlDocument_()) { + /** @type {!HTMLDocument} */ + var doc = goog.global.document; + + // If the user tries to require a new symbol after document load, + // something has gone terribly wrong. Doing a document.write would + // wipe out the page. This does not apply to the CSP-compliant method + // of writing script tags. + if (!goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING && + doc.readyState == 'complete') { + // Certain test frameworks load base.js multiple times, which tries + // to write deps.js each time. If that happens, just fail silently. + // These frameworks wipe the page between each load of base.js, so this + // is OK. + var isDeps = /\bdeps.js$/.test(src); + if (isDeps) { + return false; + } else { + throw Error('Cannot write "' + src + '" after document load'); + } + } + + if (opt_sourceText === undefined) { + if (!goog.IS_OLD_IE_) { + if (goog.ENABLE_CHROME_APP_SAFE_SCRIPT_LOADING) { + goog.appendScriptSrcNode_(src); + } else { + goog.writeScriptSrcNode_(src); + } + } else { + var state = " onreadystatechange='goog.onScriptLoad_(this, " + + ++goog.lastNonModuleScriptIndex_ + ")' "; + doc.write( + '<script type="text/javascript" src="' + src + '"' + state + + '></' + + 'script>'); + } + } else { + doc.write( + '<script type="text/javascript">' + opt_sourceText + '</' + + 'script>'); + } + return true; + } else { + return false; + } + }; + + + /** + * Determines whether the given language needs to be transpiled. + * @param {string} lang + * @return {boolean} + * @private + */ + goog.needsTranspile_ = function(lang) { + if (goog.TRANSPILE == 'always') { + return true; + } else if (goog.TRANSPILE == 'never') { + return false; + } else if (!goog.transpiledLanguages_) { + goog.transpiledLanguages_ = {'es5': true, 'es6': true, 'es6-impl': true}; + /** @preserveTry */ + try { + // Perform some quick conformance checks, to distinguish + // between browsers that support es5, es6-impl, or es6. + + // Identify ES3-only browsers by their incorrect treatment of commas. + goog.transpiledLanguages_['es5'] = eval('[1,].length!=1'); + + // As browsers mature, features will be moved from the full test + // into the impl test. This must happen before the corresponding + // features are changed in the Closure Compiler's FeatureSet object. + + // Test 1: es6-impl [FF49, Edge 13, Chrome 49] + // (a) let/const keyword, (b) class expressions, (c) Map object, + // (d) iterable arguments, (e) spread operator + var es6implTest = + 'let a={};const X=class{constructor(){}x(z){return new Map([' + + '...arguments]).get(z[0])==3}};return new X().x([a,3])'; + + // Test 2: es6 [FF50 (?), Edge 14 (?), Chrome 50] + // (a) default params (specifically shadowing locals), + // (b) destructuring, (c) block-scoped functions, + // (d) for-of (const), (e) new.target/Reflect.construct + var es6fullTest = + 'class X{constructor(){if(new.target!=String)throw 1;this.x=42}}' + + 'let q=Reflect.construct(X,[],String);if(q.x!=42||!(q instanceof ' + + 'String))throw 1;for(const a of[2,3]){if(a==2)continue;function ' + + 'f(z={a}){let a=0;return z.a}{function f(){return 0;}}return f()' + + '==3}'; + + if (eval('(()=>{"use strict";' + es6implTest + '})()')) { + goog.transpiledLanguages_['es6-impl'] = false; + } + if (eval('(()=>{"use strict";' + es6fullTest + '})()')) { + goog.transpiledLanguages_['es6'] = false; + } + } catch (err) { + } + } + return !!goog.transpiledLanguages_[lang]; + }; + + + /** @private {?Object<string, boolean>} */ + goog.transpiledLanguages_ = null; + + + /** @private {number} */ + goog.lastNonModuleScriptIndex_ = 0; + + + /** + * A readystatechange handler for legacy IE + * @param {!HTMLScriptElement} script + * @param {number} scriptIndex + * @return {boolean} + * @private + */ + goog.onScriptLoad_ = function(script, scriptIndex) { + // for now load the modules when we reach the last script, + // later allow more inter-mingling. + if (script.readyState == 'complete' && + goog.lastNonModuleScriptIndex_ == scriptIndex) { + goog.loadQueuedModules_(); + } + return true; + }; + + /** + * Resolves dependencies based on the dependencies added using addDependency + * and calls importScript_ in the correct order. + * @param {string} pathToLoad The path from which to start discovering + * dependencies. + * @private + */ + goog.writeScripts_ = function(pathToLoad) { + /** @type {!Array<string>} The scripts we need to write this time. */ + var scripts = []; + var seenScript = {}; + var deps = goog.dependencies_; + + /** @param {string} path */ + function visitNode(path) { + if (path in deps.written) { + return; + } + + // We have already visited this one. We can get here if we have cyclic + // dependencies. + if (path in deps.visited) { + return; + } + + deps.visited[path] = true; + + if (path in deps.requires) { + for (var requireName in deps.requires[path]) { + // If the required name is defined, we assume that it was already + // bootstrapped by other means. + if (!goog.isProvided_(requireName)) { + if (requireName in deps.nameToPath) { + visitNode(deps.nameToPath[requireName]); + } else { + throw Error('Undefined nameToPath for ' + requireName); + } + } + } + } + + if (!(path in seenScript)) { + seenScript[path] = true; + scripts.push(path); + } + } + + visitNode(pathToLoad); + + // record that we are going to load all these scripts. + for (var i = 0; i < scripts.length; i++) { + var path = scripts[i]; + goog.dependencies_.written[path] = true; + } + + // If a module is loaded synchronously then we need to + // clear the current inModuleLoader value, and restore it when we are + // done loading the current "requires". + var moduleState = goog.moduleLoaderState_; + goog.moduleLoaderState_ = null; + + for (var i = 0; i < scripts.length; i++) { + var path = scripts[i]; + if (path) { + var loadFlags = deps.loadFlags[path] || {}; + var needsTranspile = goog.needsTranspile_(loadFlags['lang']); + if (loadFlags['module'] == 'goog' || needsTranspile) { + goog.importProcessedScript_( + goog.basePath + path, loadFlags['module'] == 'goog', + needsTranspile); + } else { + goog.importScript_(goog.basePath + path); + } + } else { + goog.moduleLoaderState_ = moduleState; + throw Error('Undefined script input'); + } + } + + // restore the current "module loading state" + goog.moduleLoaderState_ = moduleState; + }; + + + /** + * Looks at the dependency rules and tries to determine the script file that + * fulfills a particular rule. + * @param {string} rule In the form goog.namespace.Class or project.script. + * @return {?string} Url corresponding to the rule, or null. + * @private + */ + goog.getPathFromDeps_ = function(rule) { + if (rule in goog.dependencies_.nameToPath) { + return goog.dependencies_.nameToPath[rule]; + } else { + return null; + } + }; + + goog.findBasePath_(); + + // Allow projects to manage the deps files themselves. + if (!goog.global.CLOSURE_NO_DEPS) { + goog.importScript_(goog.basePath + 'deps.js'); + } +} + + +/** + * @param {function(?):?|string} moduleDef The module definition. + */ +goog.loadModule = function(moduleDef) { + // NOTE: we allow function definitions to be either in the from + // of a string to eval (which keeps the original source intact) or + // in a eval forbidden environment (CSP) we allow a function definition + // which in its body must call {@code goog.module}, and return the exports + // of the module. + var previousState = goog.moduleLoaderState_; + try { + goog.moduleLoaderState_ = { + moduleName: undefined, + declareLegacyNamespace: false + }; + var exports; + if (goog.isFunction(moduleDef)) { + exports = moduleDef.call(undefined, {}); + } else if (goog.isString(moduleDef)) { + exports = goog.loadModuleFromSource_.call(undefined, moduleDef); + } else { + throw Error('Invalid module definition'); + } + + var moduleName = goog.moduleLoaderState_.moduleName; + if (!goog.isString(moduleName) || !moduleName) { + throw Error('Invalid module name \"' + moduleName + '\"'); + } + + // Don't seal legacy namespaces as they may be uses as a parent of + // another namespace + if (goog.moduleLoaderState_.declareLegacyNamespace) { + goog.constructNamespace_(moduleName, exports); + } else if (goog.SEAL_MODULE_EXPORTS && Object.seal) { + Object.seal(exports); + } + + goog.loadedModules_[moduleName] = exports; + } finally { + goog.moduleLoaderState_ = previousState; + } +}; + + +/** + * @private @const {function(string):?} + * + * The new type inference warns because this function has no formal + * parameters, but its jsdoc says that it takes one argument. + * (The argument is used via arguments[0], but NTI does not detect this.) + * @suppress {newCheckTypes} + */ +goog.loadModuleFromSource_ = function() { + // NOTE: we avoid declaring parameters or local variables here to avoid + // masking globals or leaking values into the module definition. + 'use strict'; + var exports = {}; + eval(arguments[0]); + return exports; +}; + + +/** + * Normalize a file path by removing redundant ".." and extraneous "." file + * path components. + * @param {string} path + * @return {string} + * @private + */ +goog.normalizePath_ = function(path) { + var components = path.split('/'); + var i = 0; + while (i < components.length) { + if (components[i] == '.') { + components.splice(i, 1); + } else if ( + i && components[i] == '..' && components[i - 1] && + components[i - 1] != '..') { + components.splice(--i, 2); + } else { + i++; + } + } + return components.join('/'); +}; + + +/** + * Loads file by synchronous XHR. Should not be used in production environments. + * @param {string} src Source URL. + * @return {?string} File contents, or null if load failed. + * @private + */ +goog.loadFileSync_ = function(src) { + if (goog.global.CLOSURE_LOAD_FILE_SYNC) { + return goog.global.CLOSURE_LOAD_FILE_SYNC(src); + } else { + try { + /** @type {XMLHttpRequest} */ + var xhr = new goog.global['XMLHttpRequest'](); + xhr.open('get', src, false); + xhr.send(); + // NOTE: Successful http: requests have a status of 200, but successful + // file: requests may have a status of zero. Any other status, or a + // thrown exception (particularly in case of file: requests) indicates + // some sort of error, which we treat as a missing or unavailable file. + return xhr.status == 0 || xhr.status == 200 ? xhr.responseText : null; + } catch (err) { + // No need to rethrow or log, since errors should show up on their own. + return null; + } + } +}; + + +/** + * Retrieve and execute a script that needs some sort of wrapping. + * @param {string} src Script source URL. + * @param {boolean} isModule Whether to load as a module. + * @param {boolean} needsTranspile Whether to transpile down to ES3. + * @private + */ +goog.retrieveAndExec_ = function(src, isModule, needsTranspile) { + if (!COMPILED) { + // The full but non-canonicalized URL for later use. + var originalPath = src; + // Canonicalize the path, removing any /./ or /../ since Chrome's debugging + // console doesn't auto-canonicalize XHR loads as it does <script> srcs. + src = goog.normalizePath_(src); + + var importScript = + goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_; + + var scriptText = goog.loadFileSync_(src); + if (scriptText == null) { + throw new Error('Load of "' + src + '" failed'); + } + + if (needsTranspile) { + scriptText = goog.transpile_.call(goog.global, scriptText, src); + } + + if (isModule) { + scriptText = goog.wrapModule_(src, scriptText); + } else { + scriptText += '\n//# sourceURL=' + src; + } + var isOldIE = goog.IS_OLD_IE_; + if (isOldIE) { + goog.dependencies_.deferred[originalPath] = scriptText; + goog.queuedModules_.push(originalPath); + } else { + importScript(src, scriptText); + } + } +}; + + +/** + * Lazily retrieves the transpiler and applies it to the source. + * @param {string} code JS code. + * @param {string} path Path to the code. + * @return {string} The transpiled code. + * @private + */ +goog.transpile_ = function(code, path) { + var jscomp = goog.global['$jscomp']; + if (!jscomp) { + goog.global['$jscomp'] = jscomp = {}; + } + var transpile = jscomp.transpile; + if (!transpile) { + var transpilerPath = goog.basePath + goog.TRANSPILER; + var transpilerCode = goog.loadFileSync_(transpilerPath); + if (transpilerCode) { + // This must be executed synchronously, since by the time we know we + // need it, we're about to load and write the ES6 code synchronously, + // so a normal script-tag load will be too slow. + eval(transpilerCode + '\n//# sourceURL=' + transpilerPath); + // Note: transpile.js reassigns goog.global['$jscomp'] so pull it again. + jscomp = goog.global['$jscomp']; + transpile = jscomp.transpile; + } + } + if (!transpile) { + // The transpiler is an optional component. If it's not available then + // replace it with a pass-through function that simply logs. + var suffix = ' requires transpilation but no transpiler was found.'; + transpile = jscomp.transpile = function(code, path) { + // TODO(user): figure out some way to get this error to show up + // in test results, noting that the failure may occur in many + // different ways, including in loadModule() before the test + // runner even comes up. + goog.logToConsole_(path + suffix); + return code; + }; + } + // Note: any transpilation errors/warnings will be logged to the console. + return transpile(code, path); +}; + + +//============================================================================== +// Language Enhancements +//============================================================================== + + +/** + * This is a "fixed" version of the typeof operator. It differs from the typeof + * operator in such a way that null returns 'null' and arrays return 'array'. + * @param {?} value The value to get the type of. + * @return {string} The name of the type. + */ +goog.typeOf = function(value) { + var s = typeof value; + if (s == 'object') { + if (value) { + // Check these first, so we can avoid calling Object.prototype.toString if + // possible. + // + // IE improperly marshals typeof across execution contexts, but a + // cross-context object will still return false for "instanceof Object". + if (value instanceof Array) { + return 'array'; + } else if (value instanceof Object) { + return s; + } + + // HACK: In order to use an Object prototype method on the arbitrary + // value, the compiler requires the value be cast to type Object, + // even though the ECMA spec explicitly allows it. + var className = Object.prototype.toString.call( + /** @type {!Object} */ (value)); + // In Firefox 3.6, attempting to access iframe window objects' length + // property throws an NS_ERROR_FAILURE, so we need to special-case it + // here. + if (className == '[object Window]') { + return 'object'; + } + + // We cannot always use constructor == Array or instanceof Array because + // different frames have different Array objects. In IE6, if the iframe + // where the array was created is destroyed, the array loses its + // prototype. Then dereferencing val.splice here throws an exception, so + // we can't use goog.isFunction. Calling typeof directly returns 'unknown' + // so that will work. In this case, this function will return false and + // most array functions will still work because the array is still + // array-like (supports length and []) even though it has lost its + // prototype. + // Mark Miller noticed that Object.prototype.toString + // allows access to the unforgeable [[Class]] property. + // 15.2.4.2 Object.prototype.toString ( ) + // When the toString method is called, the following steps are taken: + // 1. Get the [[Class]] property of this object. + // 2. Compute a string value by concatenating the three strings + // "[object ", Result(1), and "]". + // 3. Return Result(2). + // and this behavior survives the destruction of the execution context. + if ((className == '[object Array]' || + // In IE all non value types are wrapped as objects across window + // boundaries (not iframe though) so we have to do object detection + // for this edge case. + typeof value.length == 'number' && + typeof value.splice != 'undefined' && + typeof value.propertyIsEnumerable != 'undefined' && + !value.propertyIsEnumerable('splice') + + )) { + return 'array'; + } + // HACK: There is still an array case that fails. + // function ArrayImpostor() {} + // ArrayImpostor.prototype = []; + // var impostor = new ArrayImpostor; + // this can be fixed by getting rid of the fast path + // (value instanceof Array) and solely relying on + // (value && Object.prototype.toString.vall(value) === '[object Array]') + // but that would require many more function calls and is not warranted + // unless closure code is receiving objects from untrusted sources. + + // IE in cross-window calls does not correctly marshal the function type + // (it appears just as an object) so we cannot use just typeof val == + // 'function'. However, if the object has a call property, it is a + // function. + if ((className == '[object Function]' || + typeof value.call != 'undefined' && + typeof value.propertyIsEnumerable != 'undefined' && + !value.propertyIsEnumerable('call'))) { + return 'function'; + } + + } else { + return 'null'; + } + + } else if (s == 'function' && typeof value.call == 'undefined') { + // In Safari typeof nodeList returns 'function', and on Firefox typeof + // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We + // would like to return object for those and we can detect an invalid + // function by making sure that the function object has a call method. + return 'object'; + } + return s; +}; + + +/** + * Returns true if the specified value is null. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is null. + */ +goog.isNull = function(val) { + return val === null; +}; + + +/** + * Returns true if the specified value is defined and not null. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is defined and not null. + */ +goog.isDefAndNotNull = function(val) { + // Note that undefined == null. + return val != null; +}; + + +/** + * Returns true if the specified value is an array. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an array. + */ +goog.isArray = function(val) { + return goog.typeOf(val) == 'array'; +}; + + +/** + * Returns true if the object looks like an array. To qualify as array like + * the value needs to be either a NodeList or an object with a Number length + * property. As a special case, a function value is not array like, because its + * length property is fixed to correspond to the number of expected arguments. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an array. + */ +goog.isArrayLike = function(val) { + var type = goog.typeOf(val); + // We do not use goog.isObject here in order to exclude function values. + return type == 'array' || type == 'object' && typeof val.length == 'number'; +}; + + +/** + * Returns true if the object looks like a Date. To qualify as Date-like the + * value needs to be an object and have a getFullYear() function. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a like a Date. + */ +goog.isDateLike = function(val) { + return goog.isObject(val) && typeof val.getFullYear == 'function'; +}; + + +/** + * Returns true if the specified value is a string. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a string. + */ +goog.isString = function(val) { + return typeof val == 'string'; +}; + + +/** + * Returns true if the specified value is a boolean. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is boolean. + */ +goog.isBoolean = function(val) { + return typeof val == 'boolean'; +}; + + +/** + * Returns true if the specified value is a number. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a number. + */ +goog.isNumber = function(val) { + return typeof val == 'number'; +}; + + +/** + * Returns true if the specified value is a function. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is a function. + */ +goog.isFunction = function(val) { + return goog.typeOf(val) == 'function'; +}; + + +/** + * Returns true if the specified value is an object. This includes arrays and + * functions. + * @param {?} val Variable to test. + * @return {boolean} Whether variable is an object. + */ +goog.isObject = function(val) { + var type = typeof val; + return type == 'object' && val != null || type == 'function'; + // return Object(val) === val also works, but is slower, especially if val is + // not an object. +}; + + +/** + * Gets a unique ID for an object. This mutates the object so that further calls + * with the same object as a parameter returns the same value. The unique ID is + * guaranteed to be unique across the current session amongst objects that are + * passed into {@code getUid}. There is no guarantee that the ID is unique or + * consistent across sessions. It is unsafe to generate unique ID for function + * prototypes. + * + * @param {Object} obj The object to get the unique ID for. + * @return {number} The unique ID for the object. + */ +goog.getUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + + // In Opera window.hasOwnProperty exists but always returns false so we avoid + // using it. As a consequence the unique ID generated for BaseClass.prototype + // and SubClass.prototype will be the same. + return obj[goog.UID_PROPERTY_] || + (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_); +}; + + +/** + * Whether the given object is already assigned a unique ID. + * + * This does not modify the object. + * + * @param {!Object} obj The object to check. + * @return {boolean} Whether there is an assigned unique id for the object. + */ +goog.hasUid = function(obj) { + return !!obj[goog.UID_PROPERTY_]; +}; + + +/** + * Removes the unique ID from an object. This is useful if the object was + * previously mutated using {@code goog.getUid} in which case the mutation is + * undone. + * @param {Object} obj The object to remove the unique ID field from. + */ +goog.removeUid = function(obj) { + // TODO(arv): Make the type stricter, do not accept null. + + // In IE, DOM nodes are not instances of Object and throw an exception if we + // try to delete. Instead we try to use removeAttribute. + if (obj !== null && 'removeAttribute' in obj) { + obj.removeAttribute(goog.UID_PROPERTY_); + } + /** @preserveTry */ + try { + delete obj[goog.UID_PROPERTY_]; + } catch (ex) { + } +}; + + +/** + * Name for unique ID property. Initialized in a way to help avoid collisions + * with other closure JavaScript on the same page. + * @type {string} + * @private + */ +goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0); + + +/** + * Counter for UID. + * @type {number} + * @private + */ +goog.uidCounter_ = 0; + + +/** + * Adds a hash code field to an object. The hash code is unique for the + * given object. + * @param {Object} obj The object to get the hash code for. + * @return {number} The hash code for the object. + * @deprecated Use goog.getUid instead. + */ +goog.getHashCode = goog.getUid; + + +/** + * Removes the hash code field from an object. + * @param {Object} obj The object to remove the field from. + * @deprecated Use goog.removeUid instead. + */ +goog.removeHashCode = goog.removeUid; + + +/** + * Clones a value. The input may be an Object, Array, or basic type. Objects and + * arrays will be cloned recursively. + * + * WARNINGS: + * <code>goog.cloneObject</code> does not detect reference loops. Objects that + * refer to themselves will cause infinite recursion. + * + * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies + * UIDs created by <code>getUid</code> into cloned results. + * + * @param {*} obj The value to clone. + * @return {*} A clone of the input value. + * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods. + */ +goog.cloneObject = function(obj) { + var type = goog.typeOf(obj); + if (type == 'object' || type == 'array') { + if (obj.clone) { + return obj.clone(); + } + var clone = type == 'array' ? [] : {}; + for (var key in obj) { + clone[key] = goog.cloneObject(obj[key]); + } + return clone; + } + + return obj; +}; + + +/** + * A native implementation of goog.bind. + * @param {Function} fn A function to partially apply. + * @param {Object|undefined} selfObj Specifies the object which this should + * point to when the function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function bind() was + * invoked as a method of. + * @private + * @suppress {deprecated} The compiler thinks that Function.prototype.bind is + * deprecated because some people have declared a pure-JS version. + * Only the pure-JS version is truly deprecated. + */ +goog.bindNative_ = function(fn, selfObj, var_args) { + return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); +}; + + +/** + * A pure-JS implementation of goog.bind. + * @param {Function} fn A function to partially apply. + * @param {Object|undefined} selfObj Specifies the object which this should + * point to when the function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function bind() was + * invoked as a method of. + * @private + */ +goog.bindJs_ = function(fn, selfObj, var_args) { + if (!fn) { + throw new Error(); + } + + if (arguments.length > 2) { + var boundArgs = Array.prototype.slice.call(arguments, 2); + return function() { + // Prepend the bound arguments to the current arguments. + var newArgs = Array.prototype.slice.call(arguments); + Array.prototype.unshift.apply(newArgs, boundArgs); + return fn.apply(selfObj, newArgs); + }; + + } else { + return function() { return fn.apply(selfObj, arguments); }; + } +}; + + +/** + * Partially applies this function to a particular 'this object' and zero or + * more arguments. The result is a new function with some arguments of the first + * function pre-filled and the value of this 'pre-specified'. + * + * Remaining arguments specified at call-time are appended to the pre-specified + * ones. + * + * Also see: {@link #partial}. + * + * Usage: + * <pre>var barMethBound = goog.bind(myFunction, myObj, 'arg1', 'arg2'); + * barMethBound('arg3', 'arg4');</pre> + * + * @param {?function(this:T, ...)} fn A function to partially apply. + * @param {T} selfObj Specifies the object which this should point to when the + * function is run. + * @param {...*} var_args Additional arguments that are partially applied to the + * function. + * @return {!Function} A partially-applied form of the function goog.bind() was + * invoked as a method of. + * @template T + * @suppress {deprecated} See above. + */ +goog.bind = function(fn, selfObj, var_args) { + // TODO(nicksantos): narrow the type signature. + if (Function.prototype.bind && + // NOTE(nicksantos): Somebody pulled base.js into the default Chrome + // extension environment. This means that for Chrome extensions, they get + // the implementation of Function.prototype.bind that calls goog.bind + // instead of the native one. Even worse, we don't want to introduce a + // circular dependency between goog.bind and Function.prototype.bind, so + // we have to hack this to make sure it works correctly. + Function.prototype.bind.toString().indexOf('native code') != -1) { + goog.bind = goog.bindNative_; + } else { + goog.bind = goog.bindJs_; + } + return goog.bind.apply(null, arguments); +}; + + +/** + * Like goog.bind(), except that a 'this object' is not required. Useful when + * the target function is already bound. + * + * Usage: + * var g = goog.partial(f, arg1, arg2); + * g(arg3, arg4); + * + * @param {Function} fn A function to partially apply. + * @param {...*} var_args Additional arguments that are partially applied to fn. + * @return {!Function} A partially-applied form of the function goog.partial() + * was invoked as a method of. + */ +goog.partial = function(fn, var_args) { + var args = Array.prototype.slice.call(arguments, 1); + return function() { + // Clone the array (with slice()) and append additional arguments + // to the existing arguments. + var newArgs = args.slice(); + newArgs.push.apply(newArgs, arguments); + return fn.apply(this, newArgs); + }; +}; + + +/** + * Copies all the members of a source object to a target object. This method + * does not work on all browsers for all objects that contain keys such as + * toString or hasOwnProperty. Use goog.object.extend for this purpose. + * @param {Object} target Target. + * @param {Object} source Source. + */ +goog.mixin = function(target, source) { + for (var x in source) { + target[x] = source[x]; + } + + // For IE7 or lower, the for-in-loop does not contain any properties that are + // not enumerable on the prototype object (for example, isPrototypeOf from + // Object.prototype) but also it will not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). +}; + + +/** + * @return {number} An integer value representing the number of milliseconds + * between midnight, January 1, 1970 and the current time. + */ +goog.now = (goog.TRUSTED_SITE && Date.now) || (function() { + // Unary plus operator converts its operand to a number which in + // the case of + // a date is done by calling getTime(). + return +new Date(); + }); + + +/** + * Evals JavaScript in the global scope. In IE this uses execScript, other + * browsers use goog.global.eval. If goog.global.eval does not evaluate in the + * global scope (for example, in Safari), appends a script tag instead. + * Throws an exception if neither execScript or eval is defined. + * @param {string} script JavaScript string. + */ +goog.globalEval = function(script) { + if (goog.global.execScript) { + goog.global.execScript(script, 'JavaScript'); + } else if (goog.global.eval) { + // Test to see if eval works + if (goog.evalWorksForGlobals_ == null) { + goog.global.eval('var _evalTest_ = 1;'); + if (typeof goog.global['_evalTest_'] != 'undefined') { + try { + delete goog.global['_evalTest_']; + } catch (ignore) { + // Microsoft edge fails the deletion above in strict mode. + } + goog.evalWorksForGlobals_ = true; + } else { + goog.evalWorksForGlobals_ = false; + } + } + + if (goog.evalWorksForGlobals_) { + goog.global.eval(script); + } else { + /** @type {Document} */ + var doc = goog.global.document; + var scriptElt = + /** @type {!HTMLScriptElement} */ (doc.createElement('SCRIPT')); + scriptElt.type = 'text/javascript'; + scriptElt.defer = false; + // Note(user): can't use .innerHTML since "t('<test>')" will fail and + // .text doesn't work in Safari 2. Therefore we append a text node. + scriptElt.appendChild(doc.createTextNode(script)); + doc.body.appendChild(scriptElt); + doc.body.removeChild(scriptElt); + } + } else { + throw Error('goog.globalEval not available'); + } +}; + + +/** + * Indicates whether or not we can call 'eval' directly to eval code in the + * global scope. Set to a Boolean by the first call to goog.globalEval (which + * empirically tests whether eval works for globals). @see goog.globalEval + * @type {?boolean} + * @private + */ +goog.evalWorksForGlobals_ = null; + + +/** + * Optional map of CSS class names to obfuscated names used with + * goog.getCssName(). + * @private {!Object<string, string>|undefined} + * @see goog.setCssNameMapping + */ +goog.cssNameMapping_; + + +/** + * Optional obfuscation style for CSS class names. Should be set to either + * 'BY_WHOLE' or 'BY_PART' if defined. + * @type {string|undefined} + * @private + * @see goog.setCssNameMapping + */ +goog.cssNameMappingStyle_; + + +/** + * Handles strings that are intended to be used as CSS class names. + * + * This function works in tandem with @see goog.setCssNameMapping. + * + * Without any mapping set, the arguments are simple joined with a hyphen and + * passed through unaltered. + * + * When there is a mapping, there are two possible styles in which these + * mappings are used. In the BY_PART style, each part (i.e. in between hyphens) + * of the passed in css name is rewritten according to the map. In the BY_WHOLE + * style, the full css name is looked up in the map directly. If a rewrite is + * not specified by the map, the compiler will output a warning. + * + * When the mapping is passed to the compiler, it will replace calls to + * goog.getCssName with the strings from the mapping, e.g. + * var x = goog.getCssName('foo'); + * var y = goog.getCssName(this.baseClass, 'active'); + * becomes: + * var x = 'foo'; + * var y = this.baseClass + '-active'; + * + * If one argument is passed it will be processed, if two are passed only the + * modifier will be processed, as it is assumed the first argument was generated + * as a result of calling goog.getCssName. + * + * @param {string} className The class name. + * @param {string=} opt_modifier A modifier to be appended to the class name. + * @return {string} The class name or the concatenation of the class name and + * the modifier. + */ +goog.getCssName = function(className, opt_modifier) { + var getMapping = function(cssName) { + return goog.cssNameMapping_[cssName] || cssName; + }; + + var renameByParts = function(cssName) { + // Remap all the parts individually. + var parts = cssName.split('-'); + var mapped = []; + for (var i = 0; i < parts.length; i++) { + mapped.push(getMapping(parts[i])); + } + return mapped.join('-'); + }; + + var rename; + if (goog.cssNameMapping_) { + rename = + goog.cssNameMappingStyle_ == 'BY_WHOLE' ? getMapping : renameByParts; + } else { + rename = function(a) { return a; }; + } + + if (opt_modifier) { + return className + '-' + rename(opt_modifier); + } else { + return rename(className); + } +}; + + +/** + * Sets the map to check when returning a value from goog.getCssName(). Example: + * <pre> + * goog.setCssNameMapping({ + * "goog": "a", + * "disabled": "b", + * }); + * + * var x = goog.getCssName('goog'); + * // The following evaluates to: "a a-b". + * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled') + * </pre> + * When declared as a map of string literals to string literals, the JSCompiler + * will replace all calls to goog.getCssName() using the supplied map if the + * --process_closure_primitives flag is set. + * + * @param {!Object} mapping A map of strings to strings where keys are possible + * arguments to goog.getCssName() and values are the corresponding values + * that should be returned. + * @param {string=} opt_style The style of css name mapping. There are two valid + * options: 'BY_PART', and 'BY_WHOLE'. + * @see goog.getCssName for a description. + */ +goog.setCssNameMapping = function(mapping, opt_style) { + goog.cssNameMapping_ = mapping; + goog.cssNameMappingStyle_ = opt_style; +}; + + +/** + * To use CSS renaming in compiled mode, one of the input files should have a + * call to goog.setCssNameMapping() with an object literal that the JSCompiler + * can extract and use to replace all calls to goog.getCssName(). In uncompiled + * mode, JavaScript code should be loaded before this base.js file that declares + * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is + * to ensure that the mapping is loaded before any calls to goog.getCssName() + * are made in uncompiled mode. + * + * A hook for overriding the CSS name mapping. + * @type {!Object<string, string>|undefined} + */ +goog.global.CLOSURE_CSS_NAME_MAPPING; + + +if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) { + // This does not call goog.setCssNameMapping() because the JSCompiler + // requires that goog.setCssNameMapping() be called with an object literal. + goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING; +} + + +/** + * Gets a localized message. + * + * This function is a compiler primitive. If you give the compiler a localized + * message bundle, it will replace the string at compile-time with a localized + * version, and expand goog.getMsg call to a concatenated string. + * + * Messages must be initialized in the form: + * <code> + * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'}); + * </code> + * + * This function produces a string which should be treated as plain text. Use + * {@link goog.html.SafeHtmlFormatter} in conjunction with goog.getMsg to + * produce SafeHtml. + * + * @param {string} str Translatable string, places holders in the form {$foo}. + * @param {Object<string, string>=} opt_values Maps place holder name to value. + * @return {string} message with placeholders filled. + */ +goog.getMsg = function(str, opt_values) { + if (opt_values) { + str = str.replace(/\{\$([^}]+)}/g, function(match, key) { + return (opt_values != null && key in opt_values) ? opt_values[key] : + match; + }); + } + return str; +}; + + +/** + * Gets a localized message. If the message does not have a translation, gives a + * fallback message. + * + * This is useful when introducing a new message that has not yet been + * translated into all languages. + * + * This function is a compiler primitive. Must be used in the form: + * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code> + * where MSG_A and MSG_B were initialized with goog.getMsg. + * + * @param {string} a The preferred message. + * @param {string} b The fallback message. + * @return {string} The best translated message. + */ +goog.getMsgWithFallback = function(a, b) { + return a; +}; + + +/** + * Exposes an unobfuscated global namespace path for the given object. + * Note that fields of the exported object *will* be obfuscated, unless they are + * exported in turn via this function or goog.exportProperty. + * + * Also handy for making public items that are defined in anonymous closures. + * + * ex. goog.exportSymbol('public.path.Foo', Foo); + * + * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction); + * public.path.Foo.staticFunction(); + * + * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod', + * Foo.prototype.myMethod); + * new public.path.Foo().myMethod(); + * + * @param {string} publicPath Unobfuscated name to export. + * @param {*} object Object the name should point to. + * @param {Object=} opt_objectToExportTo The object to add the path to; default + * is goog.global. + */ +goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) { + goog.exportPath_(publicPath, object, opt_objectToExportTo); +}; + + +/** + * Exports a property unobfuscated into the object's namespace. + * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction); + * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); + * @param {Object} object Object whose static property is being exported. + * @param {string} publicName Unobfuscated name to export. + * @param {*} symbol Object the name should point to. + */ +goog.exportProperty = function(object, publicName, symbol) { + object[publicName] = symbol; +}; + + +/** + * Inherit the prototype methods from one constructor into another. + * + * Usage: + * <pre> + * function ParentClass(a, b) { } + * ParentClass.prototype.foo = function(a) { }; + * + * function ChildClass(a, b, c) { + * ChildClass.base(this, 'constructor', a, b); + * } + * goog.inherits(ChildClass, ParentClass); + * + * var child = new ChildClass('a', 'b', 'see'); + * child.foo(); // This works. + * </pre> + * + * @param {!Function} childCtor Child class. + * @param {!Function} parentCtor Parent class. + */ +goog.inherits = function(childCtor, parentCtor) { + /** @constructor */ + function tempCtor() {} + tempCtor.prototype = parentCtor.prototype; + childCtor.superClass_ = parentCtor.prototype; + childCtor.prototype = new tempCtor(); + /** @override */ + childCtor.prototype.constructor = childCtor; + + /** + * Calls superclass constructor/method. + * + * This function is only available if you use goog.inherits to + * express inheritance relationships between classes. + * + * NOTE: This is a replacement for goog.base and for superClass_ + * property defined in childCtor. + * + * @param {!Object} me Should always be "this". + * @param {string} methodName The method name to call. Calling + * superclass constructor can be done with the special string + * 'constructor'. + * @param {...*} var_args The arguments to pass to superclass + * method/constructor. + * @return {*} The return value of the superclass method/constructor. + */ + childCtor.base = function(me, methodName, var_args) { + // Copying using loop to avoid deop due to passing arguments object to + // function. This is faster in many JS engines as of late 2014. + var args = new Array(arguments.length - 2); + for (var i = 2; i < arguments.length; i++) { + args[i - 2] = arguments[i]; + } + return parentCtor.prototype[methodName].apply(me, args); + }; +}; + + +/** + * Call up to the superclass. + * + * If this is called from a constructor, then this calls the superclass + * constructor with arguments 1-N. + * + * If this is called from a prototype method, then you must pass the name of the + * method as the second argument to this function. If you do not, you will get a + * runtime error. This calls the superclass' method with arguments 2-N. + * + * This function only works if you use goog.inherits to express inheritance + * relationships between your classes. + * + * This function is a compiler primitive. At compile-time, the compiler will do + * macro expansion to remove a lot of the extra overhead that this function + * introduces. The compiler will also enforce a lot of the assumptions that this + * function makes, and treat it as a compiler error if you break them. + * + * @param {!Object} me Should always be "this". + * @param {*=} opt_methodName The method name if calling a super method. + * @param {...*} var_args The rest of the arguments. + * @return {*} The return value of the superclass method. + * @suppress {es5Strict} This method can not be used in strict mode, but + * all Closure Library consumers must depend on this file. + */ +goog.base = function(me, opt_methodName, var_args) { + var caller = arguments.callee.caller; + + if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) { + throw Error( + 'arguments.caller not defined. goog.base() cannot be used ' + + 'with strict mode code. See ' + + 'http://www.ecma-international.org/ecma-262/5.1/#sec-C'); + } + + if (caller.superClass_) { + // Copying using loop to avoid deop due to passing arguments object to + // function. This is faster in many JS engines as of late 2014. + var ctorArgs = new Array(arguments.length - 1); + for (var i = 1; i < arguments.length; i++) { + ctorArgs[i - 1] = arguments[i]; + } + // This is a constructor. Call the superclass constructor. + return caller.superClass_.constructor.apply(me, ctorArgs); + } + + // Copying using loop to avoid deop due to passing arguments object to + // function. This is faster in many JS engines as of late 2014. + var args = new Array(arguments.length - 2); + for (var i = 2; i < arguments.length; i++) { + args[i - 2] = arguments[i]; + } + var foundCaller = false; + for (var ctor = me.constructor; ctor; + ctor = ctor.superClass_ && ctor.superClass_.constructor) { + if (ctor.prototype[opt_methodName] === caller) { + foundCaller = true; + } else if (foundCaller) { + return ctor.prototype[opt_methodName].apply(me, args); + } + } + + // If we did not find the caller in the prototype chain, then one of two + // things happened: + // 1) The caller is an instance method. + // 2) This method was not called by the right caller. + if (me[opt_methodName] === caller) { + return me.constructor.prototype[opt_methodName].apply(me, args); + } else { + throw Error( + 'goog.base called from a method of one name ' + + 'to a method of a different name'); + } +}; + + +/** + * Allow for aliasing within scope functions. This function exists for + * uncompiled code - in compiled code the calls will be inlined and the aliases + * applied. In uncompiled code the function is simply run since the aliases as + * written are valid JavaScript. + * + * + * @param {function()} fn Function to call. This function can contain aliases + * to namespaces (e.g. "var dom = goog.dom") or classes + * (e.g. "var Timer = goog.Timer"). + */ +goog.scope = function(fn) { + if (goog.isInModuleLoader_()) { + throw Error('goog.scope is not supported within a goog.module.'); + } + fn.call(goog.global); +}; + + +/* + * To support uncompiled, strict mode bundles that use eval to divide source + * like so: + * eval('someSource;//# sourceUrl sourcefile.js'); + * We need to export the globally defined symbols "goog" and "COMPILED". + * Exporting "goog" breaks the compiler optimizations, so we required that + * be defined externally. + * NOTE: We don't use goog.exportSymbol here because we don't want to trigger + * extern generation when that compiler option is enabled. + */ +if (!COMPILED) { + goog.global['COMPILED'] = COMPILED; +} + + +//============================================================================== +// goog.defineClass implementation +//============================================================================== + + +/** + * Creates a restricted form of a Closure "class": + * - from the compiler's perspective, the instance returned from the + * constructor is sealed (no new properties may be added). This enables + * better checks. + * - the compiler will rewrite this definition to a form that is optimal + * for type checking and optimization (initially this will be a more + * traditional form). + * + * @param {Function} superClass The superclass, Object or null. + * @param {goog.defineClass.ClassDescriptor} def + * An object literal describing + * the class. It may have the following properties: + * "constructor": the constructor function + * "statics": an object literal containing methods to add to the constructor + * as "static" methods or a function that will receive the constructor + * function as its only parameter to which static properties can + * be added. + * all other properties are added to the prototype. + * @return {!Function} The class constructor. + */ +goog.defineClass = function(superClass, def) { + // TODO(johnlenz): consider making the superClass an optional parameter. + var constructor = def.constructor; + var statics = def.statics; + // Wrap the constructor prior to setting up the prototype and static methods. + if (!constructor || constructor == Object.prototype.constructor) { + constructor = function() { + throw Error('cannot instantiate an interface (no constructor defined).'); + }; + } + + var cls = goog.defineClass.createSealingConstructor_(constructor, superClass); + if (superClass) { + goog.inherits(cls, superClass); + } + + // Remove all the properties that should not be copied to the prototype. + delete def.constructor; + delete def.statics; + + goog.defineClass.applyProperties_(cls.prototype, def); + if (statics != null) { + if (statics instanceof Function) { + statics(cls); + } else { + goog.defineClass.applyProperties_(cls, statics); + } + } + + return cls; +}; + + +/** + * @typedef {{ + * constructor: (!Function|undefined), + * statics: (Object|undefined|function(Function):void) + * }} + * @suppress {missingProvide} + */ +goog.defineClass.ClassDescriptor; + + +/** + * @define {boolean} Whether the instances returned by goog.defineClass should + * be sealed when possible. + * + * When sealing is disabled the constructor function will not be wrapped by + * goog.defineClass, making it incompatible with ES6 class methods. + */ +goog.define('goog.defineClass.SEAL_CLASS_INSTANCES', goog.DEBUG); + + +/** + * If goog.defineClass.SEAL_CLASS_INSTANCES is enabled and Object.seal is + * defined, this function will wrap the constructor in a function that seals the + * results of the provided constructor function. + * + * @param {!Function} ctr The constructor whose results maybe be sealed. + * @param {Function} superClass The superclass constructor. + * @return {!Function} The replacement constructor. + * @private + */ +goog.defineClass.createSealingConstructor_ = function(ctr, superClass) { + if (!goog.defineClass.SEAL_CLASS_INSTANCES) { + // Do now wrap the constructor when sealing is disabled. Angular code + // depends on this for injection to work properly. + return ctr; + } + + // Compute whether the constructor is sealable at definition time, rather + // than when the instance is being constructed. + var superclassSealable = !goog.defineClass.isUnsealable_(superClass); + + /** + * @this {Object} + * @return {?} + */ + var wrappedCtr = function() { + // Don't seal an instance of a subclass when it calls the constructor of + // its super class as there is most likely still setup to do. + var instance = ctr.apply(this, arguments) || this; + instance[goog.UID_PROPERTY_] = instance[goog.UID_PROPERTY_]; + + if (this.constructor === wrappedCtr && superclassSealable && + Object.seal instanceof Function) { + Object.seal(instance); + } + return instance; + }; + + return wrappedCtr; +}; + + +/** + * @param {Function} ctr The constructor to test. + * @returns {boolean} Whether the constructor has been tagged as unsealable + * using goog.tagUnsealableClass. + * @private + */ +goog.defineClass.isUnsealable_ = function(ctr) { + return ctr && ctr.prototype && + ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_]; +}; + + +// TODO(johnlenz): share these values with the goog.object +/** + * The names of the fields that are defined on Object.prototype. + * @type {!Array<string>} + * @private + * @const + */ +goog.defineClass.OBJECT_PROTOTYPE_FIELDS_ = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' +]; + + +// TODO(johnlenz): share this function with the goog.object +/** + * @param {!Object} target The object to add properties to. + * @param {!Object} source The object to copy properties from. + * @private + */ +goog.defineClass.applyProperties_ = function(target, source) { + // TODO(johnlenz): update this to support ES5 getters/setters + + var key; + for (key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + + // For IE the for-in-loop does not contain any properties that are not + // enumerable on the prototype object (for example isPrototypeOf from + // Object.prototype) and it will also not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). + for (var i = 0; i < goog.defineClass.OBJECT_PROTOTYPE_FIELDS_.length; i++) { + key = goog.defineClass.OBJECT_PROTOTYPE_FIELDS_[i]; + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } +}; + + +/** + * Sealing classes breaks the older idiom of assigning properties on the + * prototype rather than in the constructor. As such, goog.defineClass + * must not seal subclasses of these old-style classes until they are fixed. + * Until then, this marks a class as "broken", instructing defineClass + * not to seal subclasses. + * @param {!Function} ctr The legacy constructor to tag as unsealable. + */ +goog.tagUnsealableClass = function(ctr) { + if (!COMPILED && goog.defineClass.SEAL_CLASS_INSTANCES) { + ctr.prototype[goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_] = true; + } +}; + + +/** + * Name for unsealable tag property. + * @const @private {string} + */ +goog.UNSEALABLE_CONSTRUCTOR_PROPERTY_ = 'goog_defineClass_legacy_unsealable'; + +goog.provide('ol'); + + +/** + * Constants defined with the define tag cannot be changed in application + * code, but can be set at compile time. + * Some reduce the size of the build in advanced compile mode. + */ + + +/** + * @define {boolean} Assume touch. Default is `false`. + */ +ol.ASSUME_TOUCH = false; + + +/** + * TODO: rename this to something having to do with tile grids + * see https://github.com/openlayers/ol3/issues/2076 + * @define {number} Default maximum zoom for default tile grids. + */ +ol.DEFAULT_MAX_ZOOM = 42; + + +/** + * @define {number} Default min zoom level for the map view. Default is `0`. + */ +ol.DEFAULT_MIN_ZOOM = 0; + + +/** + * @define {number} Default maximum allowed threshold (in pixels) for + * reprojection triangulation. Default is `0.5`. + */ +ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD = 0.5; + + +/** + * @define {number} Default tile size. + */ +ol.DEFAULT_TILE_SIZE = 256; + + +/** + * @define {string} Default WMS version. + */ +ol.DEFAULT_WMS_VERSION = '1.3.0'; + + +/** + * @define {number} Hysteresis pixels. + */ +ol.DRAG_BOX_HYSTERESIS_PIXELS = 8; + + +/** + * @define {boolean} Enable the Canvas renderer. Default is `true`. Setting + * this to false at compile time in advanced mode removes all code + * supporting the Canvas renderer from the build. + */ +ol.ENABLE_CANVAS = true; + + +/** + * @define {boolean} Enable the DOM renderer (used as a fallback where Canvas is + * not available). Default is `true`. Setting this to false at compile time + * in advanced mode removes all code supporting the DOM renderer from the + * build. + */ +ol.ENABLE_DOM = true; + + +/** + * @define {boolean} Enable rendering of ol.layer.Image based layers. Default + * is `true`. Setting this to false at compile time in advanced mode removes + * all code supporting Image layers from the build. + */ +ol.ENABLE_IMAGE = true; + + +/** + * @define {boolean} Enable Closure named colors (`goog.color.names`). + * Enabling these colors adds about 3KB uncompressed / 1.5KB compressed to + * the final build size. Default is `false`. This setting has no effect + * with Canvas renderer, which uses its own names, whether this is true or + * false. + */ +ol.ENABLE_NAMED_COLORS = false; + + +/** + * @define {boolean} Enable integration with the Proj4js library. Default is + * `true`. + */ +ol.ENABLE_PROJ4JS = true; + + +/** + * @define {boolean} Enable automatic reprojection of raster sources. Default is + * `true`. + */ +ol.ENABLE_RASTER_REPROJECTION = true; + + +/** + * @define {boolean} Enable rendering of ol.layer.Tile based layers. Default is + * `true`. Setting this to false at compile time in advanced mode removes + * all code supporting Tile layers from the build. + */ +ol.ENABLE_TILE = true; + + +/** + * @define {boolean} Enable rendering of ol.layer.Vector based layers. Default + * is `true`. Setting this to false at compile time in advanced mode removes + * all code supporting Vector layers from the build. + */ +ol.ENABLE_VECTOR = true; + + +/** + * @define {boolean} Enable rendering of ol.layer.VectorTile based layers. + * Default is `true`. Setting this to false at compile time in advanced mode + * removes all code supporting VectorTile layers from the build. + */ +ol.ENABLE_VECTOR_TILE = true; + + +/** + * @define {boolean} Enable the WebGL renderer. Default is `true`. Setting + * this to false at compile time in advanced mode removes all code + * supporting the WebGL renderer from the build. + */ +ol.ENABLE_WEBGL = true; + + +/** + * @define {number} The size in pixels of the first atlas image. Default is + * `256`. + */ +ol.INITIAL_ATLAS_SIZE = 256; + + +/** + * @define {number} The maximum size in pixels of atlas images. Default is + * `-1`, meaning it is not used (and `ol.WEBGL_MAX_TEXTURE_SIZE` is + * used instead). + */ +ol.MAX_ATLAS_SIZE = -1; + + +/** + * @define {number} Maximum mouse wheel delta. + */ +ol.MOUSEWHEELZOOM_MAXDELTA = 1; + + +/** + * @define {number} Mouse wheel timeout duration. + */ +ol.MOUSEWHEELZOOM_TIMEOUT_DURATION = 80; + + +/** + * @define {number} Maximum width and/or height extent ratio that determines + * when the overview map should be zoomed out. + */ +ol.OVERVIEWMAP_MAX_RATIO = 0.75; + + +/** + * @define {number} Minimum width and/or height extent ratio that determines + * when the overview map should be zoomed in. + */ +ol.OVERVIEWMAP_MIN_RATIO = 0.1; + + +/** + * @define {number} Maximum number of source tiles for raster reprojection of + * a single tile. + * If too many source tiles are determined to be loaded to create a single + * reprojected tile the browser can become unresponsive or even crash. + * This can happen if the developer defines projections improperly and/or + * with unlimited extents. + * If too many tiles are required, no tiles are loaded and + * `ol.TileState.ERROR` state is set. Default is `100`. + */ +ol.RASTER_REPROJECTION_MAX_SOURCE_TILES = 100; + + +/** + * @define {number} Maximum number of subdivision steps during raster + * reprojection triangulation. Prevents high memory usage and large + * number of proj4 calls (for certain transformations and areas). + * At most `2*(2^this)` triangles are created for each triangulated + * extent (tile/image). Default is `10`. + */ +ol.RASTER_REPROJECTION_MAX_SUBDIVISION = 10; + + +/** + * @define {number} Maximum allowed size of triangle relative to world width. + * When transforming corners of world extent between certain projections, + * the resulting triangulation seems to have zero error and no subdivision + * is performed. + * If the triangle width is more than this (relative to world width; 0-1), + * subdivison is forced (up to `ol.RASTER_REPROJECTION_MAX_SUBDIVISION`). + * Default is `0.25`. + */ +ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH = 0.25; + + +/** + * @define {number} Tolerance for geometry simplification in device pixels. + */ +ol.SIMPLIFY_TOLERANCE = 0.5; + + +/** + * @define {number} Texture cache high water mark. + */ +ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK = 1024; + + +/** + * The maximum supported WebGL texture size in pixels. If WebGL is not + * supported, the value is set to `undefined`. + * @const + * @type {number|undefined} + */ +ol.WEBGL_MAX_TEXTURE_SIZE; // value is set in `ol.has` + + +/** + * List of supported WebGL extensions. + * @const + * @type {Array.<string>} + */ +ol.WEBGL_EXTENSIONS; // value is set in `ol.has` + + +/** + * Inherit the prototype methods from one constructor into another. + * + * Usage: + * + * function ParentClass(a, b) { } + * ParentClass.prototype.foo = function(a) { } + * + * function ChildClass(a, b, c) { + * // Call parent constructor + * ParentClass.call(this, a, b); + * } + * ol.inherits(ChildClass, ParentClass); + * + * var child = new ChildClass('a', 'b', 'see'); + * child.foo(); // This works. + * + * @param {!Function} childCtor Child constructor. + * @param {!Function} parentCtor Parent constructor. + * @function + * @api + */ +ol.inherits = function(childCtor, parentCtor) { + childCtor.prototype = Object.create(parentCtor.prototype); + childCtor.prototype.constructor = childCtor; +}; + + +/** + * A reusable function, used e.g. as a default for callbacks. + * + * @return {undefined} Nothing. + */ +ol.nullFunction = function() {}; + + +ol.global = Function('return this')(); + +// Copyright 2009 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides a base class for custom Error objects such that the + * stack is correctly maintained. + * + * You should never need to throw goog.debug.Error(msg) directly, Error(msg) is + * sufficient. + * + */ + +goog.provide('goog.debug.Error'); + + + +/** + * Base class for custom error objects. + * @param {*=} opt_msg The message associated with the error. + * @constructor + * @extends {Error} + */ +goog.debug.Error = function(opt_msg) { + + // Attempt to ensure there is a stack trace. + if (Error.captureStackTrace) { + Error.captureStackTrace(this, goog.debug.Error); + } else { + var stack = new Error().stack; + if (stack) { + this.stack = stack; + } + } + + if (opt_msg) { + this.message = String(opt_msg); + } + + /** + * Whether to report this error to the server. Setting this to false will + * cause the error reporter to not report the error back to the server, + * which can be useful if the client knows that the error has already been + * logged on the server. + * @type {boolean} + */ + this.reportErrorToServer = true; +}; +goog.inherits(goog.debug.Error, Error); + + +/** @override */ +goog.debug.Error.prototype.name = 'CustomError'; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Definition of goog.dom.NodeType. + */ + +goog.provide('goog.dom.NodeType'); + + +/** + * Constants for the nodeType attribute in the Node interface. + * + * These constants match those specified in the Node interface. These are + * usually present on the Node object in recent browsers, but not in older + * browsers (specifically, early IEs) and thus are given here. + * + * In some browsers (early IEs), these are not defined on the Node object, + * so they are provided here. + * + * See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247 + * @enum {number} + */ +goog.dom.NodeType = { + ELEMENT: 1, + ATTRIBUTE: 2, + TEXT: 3, + CDATA_SECTION: 4, + ENTITY_REFERENCE: 5, + ENTITY: 6, + PROCESSING_INSTRUCTION: 7, + COMMENT: 8, + DOCUMENT: 9, + DOCUMENT_TYPE: 10, + DOCUMENT_FRAGMENT: 11, + NOTATION: 12 +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for string manipulation. + * @author arv@google.com (Erik Arvidsson) + */ + + +/** + * Namespace for string utilities + */ +goog.provide('goog.string'); +goog.provide('goog.string.Unicode'); + + +/** + * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps + * with detection of double-escaping as this letter is frequently used. + */ +goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false); + + +/** + * @define {boolean} Whether to force non-dom html unescaping. + */ +goog.define('goog.string.FORCE_NON_DOM_HTML_UNESCAPING', false); + + +/** + * Common Unicode string characters. + * @enum {string} + */ +goog.string.Unicode = { + NBSP: '\xa0' +}; + + +/** + * Fast prefix-checker. + * @param {string} str The string to check. + * @param {string} prefix A string to look for at the start of {@code str}. + * @return {boolean} True if {@code str} begins with {@code prefix}. + */ +goog.string.startsWith = function(str, prefix) { + return str.lastIndexOf(prefix, 0) == 0; +}; + + +/** + * Fast suffix-checker. + * @param {string} str The string to check. + * @param {string} suffix A string to look for at the end of {@code str}. + * @return {boolean} True if {@code str} ends with {@code suffix}. + */ +goog.string.endsWith = function(str, suffix) { + var l = str.length - suffix.length; + return l >= 0 && str.indexOf(suffix, l) == l; +}; + + +/** + * Case-insensitive prefix-checker. + * @param {string} str The string to check. + * @param {string} prefix A string to look for at the end of {@code str}. + * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring + * case). + */ +goog.string.caseInsensitiveStartsWith = function(str, prefix) { + return goog.string.caseInsensitiveCompare( + prefix, str.substr(0, prefix.length)) == 0; +}; + + +/** + * Case-insensitive suffix-checker. + * @param {string} str The string to check. + * @param {string} suffix A string to look for at the end of {@code str}. + * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring + * case). + */ +goog.string.caseInsensitiveEndsWith = function(str, suffix) { + return goog.string.caseInsensitiveCompare( + suffix, str.substr(str.length - suffix.length, suffix.length)) == + 0; +}; + + +/** + * Case-insensitive equality checker. + * @param {string} str1 First string to check. + * @param {string} str2 Second string to check. + * @return {boolean} True if {@code str1} and {@code str2} are the same string, + * ignoring case. + */ +goog.string.caseInsensitiveEquals = function(str1, str2) { + return str1.toLowerCase() == str2.toLowerCase(); +}; + + +/** + * Does simple python-style string substitution. + * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog". + * @param {string} str The string containing the pattern. + * @param {...*} var_args The items to substitute into the pattern. + * @return {string} A copy of {@code str} in which each occurrence of + * {@code %s} has been replaced an argument from {@code var_args}. + */ +goog.string.subs = function(str, var_args) { + var splitParts = str.split('%s'); + var returnString = ''; + + var subsArguments = Array.prototype.slice.call(arguments, 1); + while (subsArguments.length && + // Replace up to the last split part. We are inserting in the + // positions between split parts. + splitParts.length > 1) { + returnString += splitParts.shift() + subsArguments.shift(); + } + + return returnString + splitParts.join('%s'); // Join unused '%s' +}; + + +/** + * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines + * and tabs) to a single space, and strips leading and trailing whitespace. + * @param {string} str Input string. + * @return {string} A copy of {@code str} with collapsed whitespace. + */ +goog.string.collapseWhitespace = function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, ''); +}; + + +/** + * Checks if a string is empty or contains only whitespaces. + * @param {string} str The string to check. + * @return {boolean} Whether {@code str} is empty or whitespace only. + */ +goog.string.isEmptyOrWhitespace = function(str) { + // testing length == 0 first is actually slower in all browsers (about the + // same in Opera). + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return /^[\s\xa0]*$/.test(str); +}; + + +/** + * Checks if a string is empty. + * @param {string} str The string to check. + * @return {boolean} Whether {@code str} is empty. + */ +goog.string.isEmptyString = function(str) { + return str.length == 0; +}; + + +/** + * Checks if a string is empty or contains only whitespaces. + * + * TODO(user): Deprecate this when clients have been switched over to + * goog.string.isEmptyOrWhitespace. + * + * @param {string} str The string to check. + * @return {boolean} Whether {@code str} is empty or whitespace only. + */ +goog.string.isEmpty = goog.string.isEmptyOrWhitespace; + + +/** + * Checks if a string is null, undefined, empty or contains only whitespaces. + * @param {*} str The string to check. + * @return {boolean} Whether {@code str} is null, undefined, empty, or + * whitespace only. + * @deprecated Use goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str)) + * instead. + */ +goog.string.isEmptyOrWhitespaceSafe = function(str) { + return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(str)); +}; + + +/** + * Checks if a string is null, undefined, empty or contains only whitespaces. + * + * TODO(user): Deprecate this when clients have been switched over to + * goog.string.isEmptyOrWhitespaceSafe. + * + * @param {*} str The string to check. + * @return {boolean} Whether {@code str} is null, undefined, empty, or + * whitespace only. + */ +goog.string.isEmptySafe = goog.string.isEmptyOrWhitespaceSafe; + + +/** + * Checks if a string is all breaking whitespace. + * @param {string} str The string to check. + * @return {boolean} Whether the string is all breaking whitespace. + */ +goog.string.isBreakingWhitespace = function(str) { + return !/[^\t\n\r ]/.test(str); +}; + + +/** + * Checks if a string contains all letters. + * @param {string} str string to check. + * @return {boolean} True if {@code str} consists entirely of letters. + */ +goog.string.isAlpha = function(str) { + return !/[^a-zA-Z]/.test(str); +}; + + +/** + * Checks if a string contains only numbers. + * @param {*} str string to check. If not a string, it will be + * casted to one. + * @return {boolean} True if {@code str} is numeric. + */ +goog.string.isNumeric = function(str) { + return !/[^0-9]/.test(str); +}; + + +/** + * Checks if a string contains only numbers or letters. + * @param {string} str string to check. + * @return {boolean} True if {@code str} is alphanumeric. + */ +goog.string.isAlphaNumeric = function(str) { + return !/[^a-zA-Z0-9]/.test(str); +}; + + +/** + * Checks if a character is a space character. + * @param {string} ch Character to check. + * @return {boolean} True if {@code ch} is a space. + */ +goog.string.isSpace = function(ch) { + return ch == ' '; +}; + + +/** + * Checks if a character is a valid unicode character. + * @param {string} ch Character to check. + * @return {boolean} True if {@code ch} is a valid unicode character. + */ +goog.string.isUnicodeChar = function(ch) { + return ch.length == 1 && ch >= ' ' && ch <= '~' || + ch >= '\u0080' && ch <= '\uFFFD'; +}; + + +/** + * Takes a string and replaces newlines with a space. Multiple lines are + * replaced with a single space. + * @param {string} str The string from which to strip newlines. + * @return {string} A copy of {@code str} stripped of newlines. + */ +goog.string.stripNewlines = function(str) { + return str.replace(/(\r\n|\r|\n)+/g, ' '); +}; + + +/** + * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n. + * @param {string} str The string to in which to canonicalize newlines. + * @return {string} {@code str} A copy of {@code} with canonicalized newlines. + */ +goog.string.canonicalizeNewlines = function(str) { + return str.replace(/(\r\n|\r|\n)/g, '\n'); +}; + + +/** + * Normalizes whitespace in a string, replacing all whitespace chars with + * a space. + * @param {string} str The string in which to normalize whitespace. + * @return {string} A copy of {@code str} with all whitespace normalized. + */ +goog.string.normalizeWhitespace = function(str) { + return str.replace(/\xa0|\s/g, ' '); +}; + + +/** + * Normalizes spaces in a string, replacing all consecutive spaces and tabs + * with a single space. Replaces non-breaking space with a space. + * @param {string} str The string in which to normalize spaces. + * @return {string} A copy of {@code str} with all consecutive spaces and tabs + * replaced with a single space. + */ +goog.string.normalizeSpaces = function(str) { + return str.replace(/\xa0|[ \t]+/g, ' '); +}; + + +/** + * Removes the breaking spaces from the left and right of the string and + * collapses the sequences of breaking spaces in the middle into single spaces. + * The original and the result strings render the same way in HTML. + * @param {string} str A string in which to collapse spaces. + * @return {string} Copy of the string with normalized breaking spaces. + */ +goog.string.collapseBreakingSpaces = function(str) { + return str.replace(/[\t\r\n ]+/g, ' ') + .replace(/^[\t\r\n ]+|[\t\r\n ]+$/g, ''); +}; + + +/** + * Trims white spaces to the left and right of a string. + * @param {string} str The string to trim. + * @return {string} A trimmed copy of {@code str}. + */ +goog.string.trim = + (goog.TRUSTED_SITE && String.prototype.trim) ? function(str) { + return str.trim(); + } : function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s + // character class (as required by section 7.2 of the ECMAScript spec), + // we explicitly include it in the regexp to enforce consistent + // cross-browser behavior. + return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); + }; + + +/** + * Trims whitespaces at the left end of a string. + * @param {string} str The string to left trim. + * @return {string} A trimmed copy of {@code str}. + */ +goog.string.trimLeft = function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return str.replace(/^[\s\xa0]+/, ''); +}; + + +/** + * Trims whitespaces at the right end of a string. + * @param {string} str The string to right trim. + * @return {string} A trimmed copy of {@code str}. + */ +goog.string.trimRight = function(str) { + // Since IE doesn't include non-breaking-space (0xa0) in their \s character + // class (as required by section 7.2 of the ECMAScript spec), we explicitly + // include it in the regexp to enforce consistent cross-browser behavior. + return str.replace(/[\s\xa0]+$/, ''); +}; + + +/** + * A string comparator that ignores case. + * -1 = str1 less than str2 + * 0 = str1 equals str2 + * 1 = str1 greater than str2 + * + * @param {string} str1 The string to compare. + * @param {string} str2 The string to compare {@code str1} to. + * @return {number} The comparator result, as described above. + */ +goog.string.caseInsensitiveCompare = function(str1, str2) { + var test1 = String(str1).toLowerCase(); + var test2 = String(str2).toLowerCase(); + + if (test1 < test2) { + return -1; + } else if (test1 == test2) { + return 0; + } else { + return 1; + } +}; + + +/** + * Compares two strings interpreting their numeric substrings as numbers. + * + * @param {string} str1 First string. + * @param {string} str2 Second string. + * @param {!RegExp} tokenizerRegExp Splits a string into substrings of + * non-negative integers, non-numeric characters and optionally fractional + * numbers starting with a decimal point. + * @return {number} Negative if str1 < str2, 0 is str1 == str2, positive if + * str1 > str2. + * @private + */ +goog.string.numberAwareCompare_ = function(str1, str2, tokenizerRegExp) { + if (str1 == str2) { + return 0; + } + if (!str1) { + return -1; + } + if (!str2) { + return 1; + } + + // Using match to split the entire string ahead of time turns out to be faster + // for most inputs than using RegExp.exec or iterating over each character. + var tokens1 = str1.toLowerCase().match(tokenizerRegExp); + var tokens2 = str2.toLowerCase().match(tokenizerRegExp); + + var count = Math.min(tokens1.length, tokens2.length); + + for (var i = 0; i < count; i++) { + var a = tokens1[i]; + var b = tokens2[i]; + + // Compare pairs of tokens, returning if one token sorts before the other. + if (a != b) { + // Only if both tokens are integers is a special comparison required. + // Decimal numbers are sorted as strings (e.g., '.09' < '.1'). + var num1 = parseInt(a, 10); + if (!isNaN(num1)) { + var num2 = parseInt(b, 10); + if (!isNaN(num2) && num1 - num2) { + return num1 - num2; + } + } + return a < b ? -1 : 1; + } + } + + // If one string is a substring of the other, the shorter string sorts first. + if (tokens1.length != tokens2.length) { + return tokens1.length - tokens2.length; + } + + // The two strings must be equivalent except for case (perfect equality is + // tested at the head of the function.) Revert to default ASCII string + // comparison to stabilize the sort. + return str1 < str2 ? -1 : 1; +}; + + +/** + * String comparison function that handles non-negative integer numbers in a + * way humans might expect. Using this function, the string 'File 2.jpg' sorts + * before 'File 10.jpg', and 'Version 1.9' before 'Version 1.10'. The comparison + * is mostly case-insensitive, though strings that are identical except for case + * are sorted with the upper-case strings before lower-case. + * + * This comparison function is up to 50x slower than either the default or the + * case-insensitive compare. It should not be used in time-critical code, but + * should be fast enough to sort several hundred short strings (like filenames) + * with a reasonable delay. + * + * @param {string} str1 The string to compare in a numerically sensitive way. + * @param {string} str2 The string to compare {@code str1} to. + * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than + * 0 if str1 > str2. + */ +goog.string.intAwareCompare = function(str1, str2) { + return goog.string.numberAwareCompare_(str1, str2, /\d+|\D+/g); +}; + + +/** + * String comparison function that handles non-negative integer and fractional + * numbers in a way humans might expect. Using this function, the string + * 'File 2.jpg' sorts before 'File 10.jpg', and '3.14' before '3.2'. Equivalent + * to {@link goog.string.intAwareCompare} apart from the way how it interprets + * dots. + * + * @param {string} str1 The string to compare in a numerically sensitive way. + * @param {string} str2 The string to compare {@code str1} to. + * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than + * 0 if str1 > str2. + */ +goog.string.floatAwareCompare = function(str1, str2) { + return goog.string.numberAwareCompare_(str1, str2, /\d+|\.\d+|\D+/g); +}; + + +/** + * Alias for {@link goog.string.floatAwareCompare}. + * + * @param {string} str1 + * @param {string} str2 + * @return {number} + */ +goog.string.numerateCompare = goog.string.floatAwareCompare; + + +/** + * URL-encodes a string + * @param {*} str The string to url-encode. + * @return {string} An encoded copy of {@code str} that is safe for urls. + * Note that '#', ':', and other characters used to delimit portions + * of URLs *will* be encoded. + */ +goog.string.urlEncode = function(str) { + return encodeURIComponent(String(str)); +}; + + +/** + * URL-decodes the string. We need to specially handle '+'s because + * the javascript library doesn't convert them to spaces. + * @param {string} str The string to url decode. + * @return {string} The decoded {@code str}. + */ +goog.string.urlDecode = function(str) { + return decodeURIComponent(str.replace(/\+/g, ' ')); +}; + + +/** + * Converts \n to <br>s or <br />s. + * @param {string} str The string in which to convert newlines. + * @param {boolean=} opt_xml Whether to use XML compatible tags. + * @return {string} A copy of {@code str} with converted newlines. + */ +goog.string.newLineToBr = function(str, opt_xml) { + return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>'); +}; + + +/** + * Escapes double quote '"' and single quote '\'' characters in addition to + * '&', '<', and '>' so that a string can be included in an HTML tag attribute + * value within double or single quotes. + * + * It should be noted that > doesn't need to be escaped for the HTML or XML to + * be valid, but it has been decided to escape it for consistency with other + * implementations. + * + * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the + * lowercase letter "e". + * + * NOTE(user): + * HtmlEscape is often called during the generation of large blocks of HTML. + * Using statics for the regular expressions and strings is an optimization + * that can more than half the amount of time IE spends in this function for + * large apps, since strings and regexes both contribute to GC allocations. + * + * Testing for the presence of a character before escaping increases the number + * of function calls, but actually provides a speed increase for the average + * case -- since the average case often doesn't require the escaping of all 4 + * characters and indexOf() is much cheaper than replace(). + * The worst case does suffer slightly from the additional calls, therefore the + * opt_isLikelyToContainHtmlChars option has been included for situations + * where all 4 HTML entities are very likely to be present and need escaping. + * + * Some benchmarks (times tended to fluctuate +-0.05ms): + * FireFox IE6 + * (no chars / average (mix of cases) / all 4 chars) + * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80 + * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84 + * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85 + * + * An additional advantage of checking if replace actually needs to be called + * is a reduction in the number of object allocations, so as the size of the + * application grows the difference between the various methods would increase. + * + * @param {string} str string to be escaped. + * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see + * if the character needs replacing - use this option if you expect each of + * the characters to appear often. Leave false if you expect few html + * characters to occur in your strings, such as if you are escaping HTML. + * @return {string} An escaped copy of {@code str}. + */ +goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) { + + if (opt_isLikelyToContainHtmlChars) { + str = str.replace(goog.string.AMP_RE_, '&') + .replace(goog.string.LT_RE_, '<') + .replace(goog.string.GT_RE_, '>') + .replace(goog.string.QUOT_RE_, '"') + .replace(goog.string.SINGLE_QUOTE_RE_, ''') + .replace(goog.string.NULL_RE_, '�'); + if (goog.string.DETECT_DOUBLE_ESCAPING) { + str = str.replace(goog.string.E_RE_, 'e'); + } + return str; + + } else { + // quick test helps in the case when there are no chars to replace, in + // worst case this makes barely a difference to the time taken + if (!goog.string.ALL_RE_.test(str)) return str; + + // str.indexOf is faster than regex.test in this case + if (str.indexOf('&') != -1) { + str = str.replace(goog.string.AMP_RE_, '&'); + } + if (str.indexOf('<') != -1) { + str = str.replace(goog.string.LT_RE_, '<'); + } + if (str.indexOf('>') != -1) { + str = str.replace(goog.string.GT_RE_, '>'); + } + if (str.indexOf('"') != -1) { + str = str.replace(goog.string.QUOT_RE_, '"'); + } + if (str.indexOf('\'') != -1) { + str = str.replace(goog.string.SINGLE_QUOTE_RE_, '''); + } + if (str.indexOf('\x00') != -1) { + str = str.replace(goog.string.NULL_RE_, '�'); + } + if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) { + str = str.replace(goog.string.E_RE_, 'e'); + } + return str; + } +}; + + +/** + * Regular expression that matches an ampersand, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.AMP_RE_ = /&/g; + + +/** + * Regular expression that matches a less than sign, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.LT_RE_ = /</g; + + +/** + * Regular expression that matches a greater than sign, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.GT_RE_ = />/g; + + +/** + * Regular expression that matches a double quote, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.QUOT_RE_ = /"/g; + + +/** + * Regular expression that matches a single quote, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.SINGLE_QUOTE_RE_ = /'/g; + + +/** + * Regular expression that matches null character, for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.NULL_RE_ = /\x00/g; + + +/** + * Regular expression that matches a lowercase letter "e", for use in escaping. + * @const {!RegExp} + * @private + */ +goog.string.E_RE_ = /e/g; + + +/** + * Regular expression that matches any character that needs to be escaped. + * @const {!RegExp} + * @private + */ +goog.string.ALL_RE_ = + (goog.string.DETECT_DOUBLE_ESCAPING ? /[\x00&<>"'e]/ : /[\x00&<>"']/); + + +/** + * Unescapes an HTML string. + * + * @param {string} str The string to unescape. + * @return {string} An unescaped copy of {@code str}. + */ +goog.string.unescapeEntities = function(str) { + if (goog.string.contains(str, '&')) { + // We are careful not to use a DOM if we do not have one or we explicitly + // requested non-DOM html unescaping. + if (!goog.string.FORCE_NON_DOM_HTML_UNESCAPING && + 'document' in goog.global) { + return goog.string.unescapeEntitiesUsingDom_(str); + } else { + // Fall back on pure XML entities + return goog.string.unescapePureXmlEntities_(str); + } + } + return str; +}; + + +/** + * Unescapes a HTML string using the provided document. + * + * @param {string} str The string to unescape. + * @param {!Document} document A document to use in escaping the string. + * @return {string} An unescaped copy of {@code str}. + */ +goog.string.unescapeEntitiesWithDocument = function(str, document) { + if (goog.string.contains(str, '&')) { + return goog.string.unescapeEntitiesUsingDom_(str, document); + } + return str; +}; + + +/** + * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric + * entities. This function is XSS-safe and whitespace-preserving. + * @private + * @param {string} str The string to unescape. + * @param {Document=} opt_document An optional document to use for creating + * elements. If this is not specified then the default window.document + * will be used. + * @return {string} The unescaped {@code str} string. + */ +goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) { + /** @type {!Object<string, string>} */ + var seen = {'&': '&', '<': '<', '>': '>', '"': '"'}; + var div; + if (opt_document) { + div = opt_document.createElement('div'); + } else { + div = goog.global.document.createElement('div'); + } + // Match as many valid entity characters as possible. If the actual entity + // happens to be shorter, it will still work as innerHTML will return the + // trailing characters unchanged. Since the entity characters do not include + // open angle bracket, there is no chance of XSS from the innerHTML use. + // Since no whitespace is passed to innerHTML, whitespace is preserved. + return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) { + // Check for cached entity. + var value = seen[s]; + if (value) { + return value; + } + // Check for numeric entity. + if (entity.charAt(0) == '#') { + // Prefix with 0 so that hex entities (e.g. ) parse as hex numbers. + var n = Number('0' + entity.substr(1)); + if (!isNaN(n)) { + value = String.fromCharCode(n); + } + } + // Fall back to innerHTML otherwise. + if (!value) { + // Append a non-entity character to avoid a bug in Webkit that parses + // an invalid entity at the end of innerHTML text as the empty string. + div.innerHTML = s + ' '; + // Then remove the trailing character from the result. + value = div.firstChild.nodeValue.slice(0, -1); + } + // Cache and return. + return seen[s] = value; + }); +}; + + +/** + * Unescapes XML entities. + * @private + * @param {string} str The string to unescape. + * @return {string} An unescaped copy of {@code str}. + */ +goog.string.unescapePureXmlEntities_ = function(str) { + return str.replace(/&([^;]+);/g, function(s, entity) { + switch (entity) { + case 'amp': + return '&'; + case 'lt': + return '<'; + case 'gt': + return '>'; + case 'quot': + return '"'; + default: + if (entity.charAt(0) == '#') { + // Prefix with 0 so that hex entities (e.g. ) parse as hex. + var n = Number('0' + entity.substr(1)); + if (!isNaN(n)) { + return String.fromCharCode(n); + } + } + // For invalid entities we just return the entity + return s; + } + }); +}; + + +/** + * Regular expression that matches an HTML entity. + * See also HTML5: Tokenization / Tokenizing character references. + * @private + * @type {!RegExp} + */ +goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g; + + +/** + * Do escaping of whitespace to preserve spatial formatting. We use character + * entity #160 to make it safer for xml. + * @param {string} str The string in which to escape whitespace. + * @param {boolean=} opt_xml Whether to use XML compatible tags. + * @return {string} An escaped copy of {@code str}. + */ +goog.string.whitespaceEscape = function(str, opt_xml) { + // This doesn't use goog.string.preserveSpaces for backwards compatibility. + return goog.string.newLineToBr(str.replace(/ /g, '  '), opt_xml); +}; + + +/** + * Preserve spaces that would be otherwise collapsed in HTML by replacing them + * with non-breaking space Unicode characters. + * @param {string} str The string in which to preserve whitespace. + * @return {string} A copy of {@code str} with preserved whitespace. + */ +goog.string.preserveSpaces = function(str) { + return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP); +}; + + +/** + * Strip quote characters around a string. The second argument is a string of + * characters to treat as quotes. This can be a single character or a string of + * multiple character and in that case each of those are treated as possible + * quote characters. For example: + * + * <pre> + * goog.string.stripQuotes('"abc"', '"`') --> 'abc' + * goog.string.stripQuotes('`abc`', '"`') --> 'abc' + * </pre> + * + * @param {string} str The string to strip. + * @param {string} quoteChars The quote characters to strip. + * @return {string} A copy of {@code str} without the quotes. + */ +goog.string.stripQuotes = function(str, quoteChars) { + var length = quoteChars.length; + for (var i = 0; i < length; i++) { + var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i); + if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) { + return str.substring(1, str.length - 1); + } + } + return str; +}; + + +/** + * Truncates a string to a certain length and adds '...' if necessary. The + * length also accounts for the ellipsis, so a maximum length of 10 and a string + * 'Hello World!' produces 'Hello W...'. + * @param {string} str The string to truncate. + * @param {number} chars Max number of characters. + * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped + * characters from being cut off in the middle. + * @return {string} The truncated {@code str} string. + */ +goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) { + if (opt_protectEscapedCharacters) { + str = goog.string.unescapeEntities(str); + } + + if (str.length > chars) { + str = str.substring(0, chars - 3) + '...'; + } + + if (opt_protectEscapedCharacters) { + str = goog.string.htmlEscape(str); + } + + return str; +}; + + +/** + * Truncate a string in the middle, adding "..." if necessary, + * and favoring the beginning of the string. + * @param {string} str The string to truncate the middle of. + * @param {number} chars Max number of characters. + * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped + * characters from being cutoff in the middle. + * @param {number=} opt_trailingChars Optional number of trailing characters to + * leave at the end of the string, instead of truncating as close to the + * middle as possible. + * @return {string} A truncated copy of {@code str}. + */ +goog.string.truncateMiddle = function( + str, chars, opt_protectEscapedCharacters, opt_trailingChars) { + if (opt_protectEscapedCharacters) { + str = goog.string.unescapeEntities(str); + } + + if (opt_trailingChars && str.length > chars) { + if (opt_trailingChars > chars) { + opt_trailingChars = chars; + } + var endPoint = str.length - opt_trailingChars; + var startPoint = chars - opt_trailingChars; + str = str.substring(0, startPoint) + '...' + str.substring(endPoint); + } else if (str.length > chars) { + // Favor the beginning of the string: + var half = Math.floor(chars / 2); + var endPos = str.length - half; + half += chars % 2; + str = str.substring(0, half) + '...' + str.substring(endPos); + } + + if (opt_protectEscapedCharacters) { + str = goog.string.htmlEscape(str); + } + + return str; +}; + + +/** + * Special chars that need to be escaped for goog.string.quote. + * @private {!Object<string, string>} + */ +goog.string.specialEscapeChars_ = { + '\0': '\\0', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '\x0B': '\\x0B', // '\v' is not supported in JScript + '"': '\\"', + '\\': '\\\\', + // To support the use case of embedding quoted strings inside of script + // tags, we have to make sure HTML comments and opening/closing script tags do + // not appear in the resulting string. The specific strings that must be + // escaped are documented at: + // http://www.w3.org/TR/html51/semantics.html#restrictions-for-contents-of-script-elements + '<': '\x3c' +}; + + +/** + * Character mappings used internally for goog.string.escapeChar. + * @private {!Object<string, string>} + */ +goog.string.jsEscapeCache_ = { + '\'': '\\\'' +}; + + +/** + * Encloses a string in double quotes and escapes characters so that the + * string is a valid JS string. The resulting string is safe to embed in + * `<script>` tags as "<" is escaped. + * @param {string} s The string to quote. + * @return {string} A copy of {@code s} surrounded by double quotes. + */ +goog.string.quote = function(s) { + s = String(s); + var sb = ['"']; + for (var i = 0; i < s.length; i++) { + var ch = s.charAt(i); + var cc = ch.charCodeAt(0); + sb[i + 1] = goog.string.specialEscapeChars_[ch] || + ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch)); + } + sb.push('"'); + return sb.join(''); +}; + + +/** + * Takes a string and returns the escaped string for that character. + * @param {string} str The string to escape. + * @return {string} An escaped string representing {@code str}. + */ +goog.string.escapeString = function(str) { + var sb = []; + for (var i = 0; i < str.length; i++) { + sb[i] = goog.string.escapeChar(str.charAt(i)); + } + return sb.join(''); +}; + + +/** + * Takes a character and returns the escaped string for that character. For + * example escapeChar(String.fromCharCode(15)) -> "\\x0E". + * @param {string} c The character to escape. + * @return {string} An escaped string representing {@code c}. + */ +goog.string.escapeChar = function(c) { + if (c in goog.string.jsEscapeCache_) { + return goog.string.jsEscapeCache_[c]; + } + + if (c in goog.string.specialEscapeChars_) { + return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c]; + } + + var rv = c; + var cc = c.charCodeAt(0); + if (cc > 31 && cc < 127) { + rv = c; + } else { + // tab is 9 but handled above + if (cc < 256) { + rv = '\\x'; + if (cc < 16 || cc > 256) { + rv += '0'; + } + } else { + rv = '\\u'; + if (cc < 4096) { // \u1000 + rv += '0'; + } + } + rv += cc.toString(16).toUpperCase(); + } + + return goog.string.jsEscapeCache_[c] = rv; +}; + + +/** + * Determines whether a string contains a substring. + * @param {string} str The string to search. + * @param {string} subString The substring to search for. + * @return {boolean} Whether {@code str} contains {@code subString}. + */ +goog.string.contains = function(str, subString) { + return str.indexOf(subString) != -1; +}; + + +/** + * Determines whether a string contains a substring, ignoring case. + * @param {string} str The string to search. + * @param {string} subString The substring to search for. + * @return {boolean} Whether {@code str} contains {@code subString}. + */ +goog.string.caseInsensitiveContains = function(str, subString) { + return goog.string.contains(str.toLowerCase(), subString.toLowerCase()); +}; + + +/** + * Returns the non-overlapping occurrences of ss in s. + * If either s or ss evalutes to false, then returns zero. + * @param {string} s The string to look in. + * @param {string} ss The string to look for. + * @return {number} Number of occurrences of ss in s. + */ +goog.string.countOf = function(s, ss) { + return s && ss ? s.split(ss).length - 1 : 0; +}; + + +/** + * Removes a substring of a specified length at a specific + * index in a string. + * @param {string} s The base string from which to remove. + * @param {number} index The index at which to remove the substring. + * @param {number} stringLength The length of the substring to remove. + * @return {string} A copy of {@code s} with the substring removed or the full + * string if nothing is removed or the input is invalid. + */ +goog.string.removeAt = function(s, index, stringLength) { + var resultStr = s; + // If the index is greater or equal to 0 then remove substring + if (index >= 0 && index < s.length && stringLength > 0) { + resultStr = s.substr(0, index) + + s.substr(index + stringLength, s.length - index - stringLength); + } + return resultStr; +}; + + +/** + * Removes the first occurrence of a substring from a string. + * @param {string} s The base string from which to remove. + * @param {string} ss The string to remove. + * @return {string} A copy of {@code s} with {@code ss} removed or the full + * string if nothing is removed. + */ +goog.string.remove = function(s, ss) { + var re = new RegExp(goog.string.regExpEscape(ss), ''); + return s.replace(re, ''); +}; + + +/** + * Removes all occurrences of a substring from a string. + * @param {string} s The base string from which to remove. + * @param {string} ss The string to remove. + * @return {string} A copy of {@code s} with {@code ss} removed or the full + * string if nothing is removed. + */ +goog.string.removeAll = function(s, ss) { + var re = new RegExp(goog.string.regExpEscape(ss), 'g'); + return s.replace(re, ''); +}; + + +/** + * Escapes characters in the string that are not safe to use in a RegExp. + * @param {*} s The string to escape. If not a string, it will be casted + * to one. + * @return {string} A RegExp safe, escaped copy of {@code s}. + */ +goog.string.regExpEscape = function(s) { + return String(s) + .replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1') + .replace(/\x08/g, '\\x08'); +}; + + +/** + * Repeats a string n times. + * @param {string} string The string to repeat. + * @param {number} length The number of times to repeat. + * @return {string} A string containing {@code length} repetitions of + * {@code string}. + */ +goog.string.repeat = (String.prototype.repeat) ? function(string, length) { + // The native method is over 100 times faster than the alternative. + return string.repeat(length); +} : function(string, length) { + return new Array(length + 1).join(string); +}; + + +/** + * Pads number to given length and optionally rounds it to a given precision. + * For example: + * <pre>padNumber(1.25, 2, 3) -> '01.250' + * padNumber(1.25, 2) -> '01.25' + * padNumber(1.25, 2, 1) -> '01.3' + * padNumber(1.25, 0) -> '1.25'</pre> + * + * @param {number} num The number to pad. + * @param {number} length The desired length. + * @param {number=} opt_precision The desired precision. + * @return {string} {@code num} as a string with the given options. + */ +goog.string.padNumber = function(num, length, opt_precision) { + var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num); + var index = s.indexOf('.'); + if (index == -1) { + index = s.length; + } + return goog.string.repeat('0', Math.max(0, length - index)) + s; +}; + + +/** + * Returns a string representation of the given object, with + * null and undefined being returned as the empty string. + * + * @param {*} obj The object to convert. + * @return {string} A string representation of the {@code obj}. + */ +goog.string.makeSafe = function(obj) { + return obj == null ? '' : String(obj); +}; + + +/** + * Concatenates string expressions. This is useful + * since some browsers are very inefficient when it comes to using plus to + * concat strings. Be careful when using null and undefined here since + * these will not be included in the result. If you need to represent these + * be sure to cast the argument to a String first. + * For example: + * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd' + * buildString(null, undefined) -> '' + * </pre> + * @param {...*} var_args A list of strings to concatenate. If not a string, + * it will be casted to one. + * @return {string} The concatenation of {@code var_args}. + */ +goog.string.buildString = function(var_args) { + return Array.prototype.join.call(arguments, ''); +}; + + +/** + * Returns a string with at least 64-bits of randomness. + * + * Doesn't trust Javascript's random function entirely. Uses a combination of + * random and current timestamp, and then encodes the string in base-36 to + * make it shorter. + * + * @return {string} A random string, e.g. sn1s7vb4gcic. + */ +goog.string.getRandomString = function() { + var x = 2147483648; + return Math.floor(Math.random() * x).toString(36) + + Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36); +}; + + +/** + * Compares two version numbers. + * + * @param {string|number} version1 Version of first item. + * @param {string|number} version2 Version of second item. + * + * @return {number} 1 if {@code version1} is higher. + * 0 if arguments are equal. + * -1 if {@code version2} is higher. + */ +goog.string.compareVersions = function(version1, version2) { + var order = 0; + // Trim leading and trailing whitespace and split the versions into + // subversions. + var v1Subs = goog.string.trim(String(version1)).split('.'); + var v2Subs = goog.string.trim(String(version2)).split('.'); + var subCount = Math.max(v1Subs.length, v2Subs.length); + + // Iterate over the subversions, as long as they appear to be equivalent. + for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) { + var v1Sub = v1Subs[subIdx] || ''; + var v2Sub = v2Subs[subIdx] || ''; + + // Split the subversions into pairs of numbers and qualifiers (like 'b'). + // Two different RegExp objects are needed because they are both using + // the 'g' flag. + var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g'); + var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g'); + do { + var v1Comp = v1CompParser.exec(v1Sub) || ['', '', '']; + var v2Comp = v2CompParser.exec(v2Sub) || ['', '', '']; + // Break if there are no more matches. + if (v1Comp[0].length == 0 && v2Comp[0].length == 0) { + break; + } + + // Parse the numeric part of the subversion. A missing number is + // equivalent to 0. + var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10); + var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10); + + // Compare the subversion components. The number has the highest + // precedence. Next, if the numbers are equal, a subversion without any + // qualifier is always higher than a subversion with any qualifier. Next, + // the qualifiers are compared as strings. + order = goog.string.compareElements_(v1CompNum, v2CompNum) || + goog.string.compareElements_( + v1Comp[2].length == 0, v2Comp[2].length == 0) || + goog.string.compareElements_(v1Comp[2], v2Comp[2]); + // Stop as soon as an inequality is discovered. + } while (order == 0); + } + + return order; +}; + + +/** + * Compares elements of a version number. + * + * @param {string|number|boolean} left An element from a version number. + * @param {string|number|boolean} right An element from a version number. + * + * @return {number} 1 if {@code left} is higher. + * 0 if arguments are equal. + * -1 if {@code right} is higher. + * @private + */ +goog.string.compareElements_ = function(left, right) { + if (left < right) { + return -1; + } else if (left > right) { + return 1; + } + return 0; +}; + + +/** + * String hash function similar to java.lang.String.hashCode(). + * The hash code for a string is computed as + * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1], + * where s[i] is the ith character of the string and n is the length of + * the string. We mod the result to make it between 0 (inclusive) and 2^32 + * (exclusive). + * @param {string} str A string. + * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32 + * (exclusive). The empty string returns 0. + */ +goog.string.hashCode = function(str) { + var result = 0; + for (var i = 0; i < str.length; ++i) { + // Normalize to 4 byte range, 0 ... 2^32. + result = (31 * result + str.charCodeAt(i)) >>> 0; + } + return result; +}; + + +/** + * The most recent unique ID. |0 is equivalent to Math.floor in this case. + * @type {number} + * @private + */ +goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0; + + +/** + * Generates and returns a string which is unique in the current document. + * This is useful, for example, to create unique IDs for DOM elements. + * @return {string} A unique id. + */ +goog.string.createUniqueString = function() { + return 'goog_' + goog.string.uniqueStringCounter_++; +}; + + +/** + * Converts the supplied string to a number, which may be Infinity or NaN. + * This function strips whitespace: (toNumber(' 123') === 123) + * This function accepts scientific notation: (toNumber('1e1') === 10) + * + * This is better than Javascript's built-in conversions because, sadly: + * (Number(' ') === 0) and (parseFloat('123a') === 123) + * + * @param {string} str The string to convert. + * @return {number} The number the supplied string represents, or NaN. + */ +goog.string.toNumber = function(str) { + var num = Number(str); + if (num == 0 && goog.string.isEmptyOrWhitespace(str)) { + return NaN; + } + return num; +}; + + +/** + * Returns whether the given string is lower camel case (e.g. "isFooBar"). + * + * Note that this assumes the string is entirely letters. + * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms + * + * @param {string} str String to test. + * @return {boolean} Whether the string is lower camel case. + */ +goog.string.isLowerCamelCase = function(str) { + return /^[a-z]+([A-Z][a-z]*)*$/.test(str); +}; + + +/** + * Returns whether the given string is upper camel case (e.g. "FooBarBaz"). + * + * Note that this assumes the string is entirely letters. + * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms + * + * @param {string} str String to test. + * @return {boolean} Whether the string is upper camel case. + */ +goog.string.isUpperCamelCase = function(str) { + return /^([A-Z][a-z]*)+$/.test(str); +}; + + +/** + * Converts a string from selector-case to camelCase (e.g. from + * "multi-part-string" to "multiPartString"), useful for converting + * CSS selectors and HTML dataset keys to their equivalent JS properties. + * @param {string} str The string in selector-case form. + * @return {string} The string in camelCase form. + */ +goog.string.toCamelCase = function(str) { + return String(str).replace( + /\-([a-z])/g, function(all, match) { return match.toUpperCase(); }); +}; + + +/** + * Converts a string from camelCase to selector-case (e.g. from + * "multiPartString" to "multi-part-string"), useful for converting JS + * style and dataset properties to equivalent CSS selectors and HTML keys. + * @param {string} str The string in camelCase form. + * @return {string} The string in selector-case form. + */ +goog.string.toSelectorCase = function(str) { + return String(str).replace(/([A-Z])/g, '-$1').toLowerCase(); +}; + + +/** + * Converts a string into TitleCase. First character of the string is always + * capitalized in addition to the first letter of every subsequent word. + * Words are delimited by one or more whitespaces by default. Custom delimiters + * can optionally be specified to replace the default, which doesn't preserve + * whitespace delimiters and instead must be explicitly included if needed. + * + * Default delimiter => " ": + * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree' + * goog.string.toTitleCase('one two three') => 'One Two Three' + * goog.string.toTitleCase(' one two ') => ' One Two ' + * goog.string.toTitleCase('one_two_three') => 'One_two_three' + * goog.string.toTitleCase('one-two-three') => 'One-two-three' + * + * Custom delimiter => "_-.": + * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree' + * goog.string.toTitleCase('one two three', '_-.') => 'One two three' + * goog.string.toTitleCase(' one two ', '_-.') => ' one two ' + * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three' + * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three' + * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three' + * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three' + * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three' + * + * @param {string} str String value in camelCase form. + * @param {string=} opt_delimiters Custom delimiter character set used to + * distinguish words in the string value. Each character represents a + * single delimiter. When provided, default whitespace delimiter is + * overridden and must be explicitly included if needed. + * @return {string} String value in TitleCase form. + */ +goog.string.toTitleCase = function(str, opt_delimiters) { + var delimiters = goog.isString(opt_delimiters) ? + goog.string.regExpEscape(opt_delimiters) : + '\\s'; + + // For IE8, we need to prevent using an empty character set. Otherwise, + // incorrect matching will occur. + delimiters = delimiters ? '|[' + delimiters + ']+' : ''; + + var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g'); + return str.replace( + regexp, function(all, p1, p2) { return p1 + p2.toUpperCase(); }); +}; + + +/** + * Capitalizes a string, i.e. converts the first letter to uppercase + * and all other letters to lowercase, e.g.: + * + * goog.string.capitalize('one') => 'One' + * goog.string.capitalize('ONE') => 'One' + * goog.string.capitalize('one two') => 'One two' + * + * Note that this function does not trim initial whitespace. + * + * @param {string} str String value to capitalize. + * @return {string} String value with first letter in uppercase. + */ +goog.string.capitalize = function(str) { + return String(str.charAt(0)).toUpperCase() + + String(str.substr(1)).toLowerCase(); +}; + + +/** + * Parse a string in decimal or hexidecimal ('0xFFFF') form. + * + * To parse a particular radix, please use parseInt(string, radix) directly. See + * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt + * + * This is a wrapper for the built-in parseInt function that will only parse + * numbers as base 10 or base 16. Some JS implementations assume strings + * starting with "0" are intended to be octal. ES3 allowed but discouraged + * this behavior. ES5 forbids it. This function emulates the ES5 behavior. + * + * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj + * + * @param {string|number|null|undefined} value The value to be parsed. + * @return {number} The number, parsed. If the string failed to parse, this + * will be NaN. + */ +goog.string.parseInt = function(value) { + // Force finite numbers to strings. + if (isFinite(value)) { + value = String(value); + } + + if (goog.isString(value)) { + // If the string starts with '0x' or '-0x', parse as hex. + return /^\s*-?0x/i.test(value) ? parseInt(value, 16) : parseInt(value, 10); + } + + return NaN; +}; + + +/** + * Splits a string on a separator a limited number of times. + * + * This implementation is more similar to Python or Java, where the limit + * parameter specifies the maximum number of splits rather than truncating + * the number of results. + * + * See http://docs.python.org/2/library/stdtypes.html#str.split + * See JavaDoc: http://goo.gl/F2AsY + * See Mozilla reference: http://goo.gl/dZdZs + * + * @param {string} str String to split. + * @param {string} separator The separator. + * @param {number} limit The limit to the number of splits. The resulting array + * will have a maximum length of limit+1. Negative numbers are the same + * as zero. + * @return {!Array<string>} The string, split. + */ +goog.string.splitLimit = function(str, separator, limit) { + var parts = str.split(separator); + var returnVal = []; + + // Only continue doing this while we haven't hit the limit and we have + // parts left. + while (limit > 0 && parts.length) { + returnVal.push(parts.shift()); + limit--; + } + + // If there are remaining parts, append them to the end. + if (parts.length) { + returnVal.push(parts.join(separator)); + } + + return returnVal; +}; + + +/** + * Finds the characters to the right of the last instance of any separator + * + * This function is similar to goog.string.path.baseName, except it can take a + * list of characters to split the string on. It will return the rightmost + * grouping of characters to the right of any separator as a left-to-right + * oriented string. + * + * @see goog.string.path.baseName + * @param {string} str The string + * @param {string|!Array<string>} separators A list of separator characters + * @return {string} The last part of the string with respect to the separators + */ +goog.string.lastComponent = function(str, separators) { + if (!separators) { + return str; + } else if (typeof separators == 'string') { + separators = [separators]; + } + + var lastSeparatorIndex = -1; + for (var i = 0; i < separators.length; i++) { + if (separators[i] == '') { + continue; + } + var currentSeparatorIndex = str.lastIndexOf(separators[i]); + if (currentSeparatorIndex > lastSeparatorIndex) { + lastSeparatorIndex = currentSeparatorIndex; + } + } + if (lastSeparatorIndex == -1) { + return str; + } + return str.slice(lastSeparatorIndex + 1); +}; + + +/** + * Computes the Levenshtein edit distance between two strings. + * @param {string} a + * @param {string} b + * @return {number} The edit distance between the two strings. + */ +goog.string.editDistance = function(a, b) { + var v0 = []; + var v1 = []; + + if (a == b) { + return 0; + } + + if (!a.length || !b.length) { + return Math.max(a.length, b.length); + } + + for (var i = 0; i < b.length + 1; i++) { + v0[i] = i; + } + + for (var i = 0; i < a.length; i++) { + v1[0] = i + 1; + + for (var j = 0; j < b.length; j++) { + var cost = Number(a[i] != b[j]); + // Cost for the substring is the minimum of adding one character, removing + // one character, or a swap. + v1[j + 1] = Math.min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost); + } + + for (var j = 0; j < v0.length; j++) { + v0[j] = v1[j]; + } + } + + return v1[b.length]; +}; + +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities to check the preconditions, postconditions and + * invariants runtime. + * + * Methods in this package should be given special treatment by the compiler + * for type-inference. For example, <code>goog.asserts.assert(foo)</code> + * will restrict <code>foo</code> to a truthy value. + * + * The compiler has an option to disable asserts. So code like: + * <code> + * var x = goog.asserts.assert(foo()); goog.asserts.assert(bar()); + * </code> + * will be transformed into: + * <code> + * var x = foo(); + * </code> + * The compiler will leave in foo() (because its return value is used), + * but it will remove bar() because it assumes it does not have side-effects. + * + * @author agrieve@google.com (Andrew Grieve) + */ + +goog.provide('goog.asserts'); +goog.provide('goog.asserts.AssertionError'); + +goog.require('goog.debug.Error'); +goog.require('goog.dom.NodeType'); +goog.require('goog.string'); + + +/** + * @define {boolean} Whether to strip out asserts or to leave them in. + */ +goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG); + + + +/** + * Error object for failed assertions. + * @param {string} messagePattern The pattern that was used to form message. + * @param {!Array<*>} messageArgs The items to substitute into the pattern. + * @constructor + * @extends {goog.debug.Error} + * @final + */ +goog.asserts.AssertionError = function(messagePattern, messageArgs) { + messageArgs.unshift(messagePattern); + goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs)); + // Remove the messagePattern afterwards to avoid permanently modifying the + // passed in array. + messageArgs.shift(); + + /** + * The message pattern used to format the error message. Error handlers can + * use this to uniquely identify the assertion. + * @type {string} + */ + this.messagePattern = messagePattern; +}; +goog.inherits(goog.asserts.AssertionError, goog.debug.Error); + + +/** @override */ +goog.asserts.AssertionError.prototype.name = 'AssertionError'; + + +/** + * The default error handler. + * @param {!goog.asserts.AssertionError} e The exception to be handled. + */ +goog.asserts.DEFAULT_ERROR_HANDLER = function(e) { + throw e; +}; + + +/** + * The handler responsible for throwing or logging assertion errors. + * @private {function(!goog.asserts.AssertionError)} + */ +goog.asserts.errorHandler_ = goog.asserts.DEFAULT_ERROR_HANDLER; + + +/** + * Throws an exception with the given message and "Assertion failed" prefixed + * onto it. + * @param {string} defaultMessage The message to use if givenMessage is empty. + * @param {Array<*>} defaultArgs The substitution arguments for defaultMessage. + * @param {string|undefined} givenMessage Message supplied by the caller. + * @param {Array<*>} givenArgs The substitution arguments for givenMessage. + * @throws {goog.asserts.AssertionError} When the value is not a number. + * @private + */ +goog.asserts.doAssertFailure_ = function( + defaultMessage, defaultArgs, givenMessage, givenArgs) { + var message = 'Assertion failed'; + if (givenMessage) { + message += ': ' + givenMessage; + var args = givenArgs; + } else if (defaultMessage) { + message += ': ' + defaultMessage; + args = defaultArgs; + } + // The '' + works around an Opera 10 bug in the unit tests. Without it, + // a stack trace is added to var message above. With this, a stack trace is + // not added until this line (it causes the extra garbage to be added after + // the assertion message instead of in the middle of it). + var e = new goog.asserts.AssertionError('' + message, args || []); + goog.asserts.errorHandler_(e); +}; + + +/** + * Sets a custom error handler that can be used to customize the behavior of + * assertion failures, for example by turning all assertion failures into log + * messages. + * @param {function(!goog.asserts.AssertionError)} errorHandler + */ +goog.asserts.setErrorHandler = function(errorHandler) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.errorHandler_ = errorHandler; + } +}; + + +/** + * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is + * true. + * @template T + * @param {T} condition The condition to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {T} The value of the condition. + * @throws {goog.asserts.AssertionError} When the condition evaluates to false. + */ +goog.asserts.assert = function(condition, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !condition) { + goog.asserts.doAssertFailure_( + '', null, opt_message, Array.prototype.slice.call(arguments, 2)); + } + return condition; +}; + + +/** + * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case + * when we want to add a check in the unreachable area like switch-case + * statement: + * + * <pre> + * switch(type) { + * case FOO: doSomething(); break; + * case BAR: doSomethingElse(); break; + * default: goog.asserts.fail('Unrecognized type: ' + type); + * // We have only 2 types - "default:" section is unreachable code. + * } + * </pre> + * + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @throws {goog.asserts.AssertionError} Failure. + */ +goog.asserts.fail = function(opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS) { + goog.asserts.errorHandler_( + new goog.asserts.AssertionError( + 'Failure' + (opt_message ? ': ' + opt_message : ''), + Array.prototype.slice.call(arguments, 1))); + } +}; + + +/** + * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {number} The value, guaranteed to be a number when asserts enabled. + * @throws {goog.asserts.AssertionError} When the value is not a number. + */ +goog.asserts.assertNumber = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) { + goog.asserts.doAssertFailure_( + 'Expected number but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {number} */ (value); +}; + + +/** + * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {string} The value, guaranteed to be a string when asserts enabled. + * @throws {goog.asserts.AssertionError} When the value is not a string. + */ +goog.asserts.assertString = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) { + goog.asserts.doAssertFailure_( + 'Expected string but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {string} */ (value); +}; + + +/** + * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Function} The value, guaranteed to be a function when asserts + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not a function. + */ +goog.asserts.assertFunction = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) { + goog.asserts.doAssertFailure_( + 'Expected function but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Function} */ (value); +}; + + +/** + * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Object} The value, guaranteed to be a non-null object. + * @throws {goog.asserts.AssertionError} When the value is not an object. + */ +goog.asserts.assertObject = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) { + goog.asserts.doAssertFailure_( + 'Expected object but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Object} */ (value); +}; + + +/** + * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Array<?>} The value, guaranteed to be a non-null array. + * @throws {goog.asserts.AssertionError} When the value is not an array. + */ +goog.asserts.assertArray = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) { + goog.asserts.doAssertFailure_( + 'Expected array but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Array<?>} */ (value); +}; + + +/** + * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {boolean} The value, guaranteed to be a boolean when asserts are + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not a boolean. + */ +goog.asserts.assertBoolean = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) { + goog.asserts.doAssertFailure_( + 'Expected boolean but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {boolean} */ (value); +}; + + +/** + * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true. + * @param {*} value The value to check. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @return {!Element} The value, likely to be a DOM Element when asserts are + * enabled. + * @throws {goog.asserts.AssertionError} When the value is not an Element. + */ +goog.asserts.assertElement = function(value, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && + (!goog.isObject(value) || value.nodeType != goog.dom.NodeType.ELEMENT)) { + goog.asserts.doAssertFailure_( + 'Expected Element but got %s: %s.', [goog.typeOf(value), value], + opt_message, Array.prototype.slice.call(arguments, 2)); + } + return /** @type {!Element} */ (value); +}; + + +/** + * Checks if the value is an instance of the user-defined type if + * goog.asserts.ENABLE_ASSERTS is true. + * + * The compiler may tighten the type returned by this function. + * + * @param {?} value The value to check. + * @param {function(new: T, ...)} type A user-defined constructor. + * @param {string=} opt_message Error message in case of failure. + * @param {...*} var_args The items to substitute into the failure message. + * @throws {goog.asserts.AssertionError} When the value is not an instance of + * type. + * @return {T} + * @template T + */ +goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) { + if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) { + goog.asserts.doAssertFailure_( + 'Expected instanceof %s but got %s.', + [goog.asserts.getType_(type), goog.asserts.getType_(value)], + opt_message, Array.prototype.slice.call(arguments, 3)); + } + return value; +}; + + +/** + * Checks that no enumerable keys are present in Object.prototype. Such keys + * would break most code that use {@code for (var ... in ...)} loops. + */ +goog.asserts.assertObjectPrototypeIsIntact = function() { + for (var key in Object.prototype) { + goog.asserts.fail(key + ' should not be enumerable in Object.prototype.'); + } +}; + + +/** + * Returns the type of a value. If a constructor is passed, and a suitable + * string cannot be found, 'unknown type name' will be returned. + * @param {*} value A constructor, object, or primitive. + * @return {string} The best display name for the value, or 'unknown type name'. + * @private + */ +goog.asserts.getType_ = function(value) { + if (value instanceof Function) { + return value.displayName || value.name || 'unknown type name'; + } else if (value instanceof Object) { + return value.constructor.displayName || value.constructor.name || + Object.prototype.toString.call(value); + } else { + return value === null ? 'null' : typeof value; + } +}; + +goog.provide('ol.math'); + +goog.require('goog.asserts'); + + +/** + * Takes a number and clamps it to within the provided bounds. + * @param {number} value The input number. + * @param {number} min The minimum value to return. + * @param {number} max The maximum value to return. + * @return {number} The input number if it is within bounds, or the nearest + * number within the bounds. + */ +ol.math.clamp = function(value, min, max) { + return Math.min(Math.max(value, min), max); +}; + + +/** + * Return the hyperbolic cosine of a given number. The method will use the + * native `Math.cosh` function if it is available, otherwise the hyperbolic + * cosine will be calculated via the reference implementation of the Mozilla + * developer network. + * + * @param {number} x X. + * @return {number} Hyperbolic cosine of x. + */ +ol.math.cosh = (function() { + // Wrapped in a iife, to save the overhead of checking for the native + // implementation on every invocation. + var cosh; + if ('cosh' in Math) { + // The environment supports the native Math.cosh function, use it… + cosh = Math.cosh; + } else { + // … else, use the reference implementation of MDN: + cosh = function(x) { + var y = Math.exp(x); + return (y + 1 / y) / 2; + }; + } + return cosh; +}()); + + +/** + * @param {number} x X. + * @return {number} The smallest power of two greater than or equal to x. + */ +ol.math.roundUpToPowerOfTwo = function(x) { + goog.asserts.assert(0 < x, 'x should be larger than 0'); + return Math.pow(2, Math.ceil(Math.log(x) / Math.LN2)); +}; + + +/** + * Returns the square of the closest distance between the point (x, y) and the + * line segment (x1, y1) to (x2, y2). + * @param {number} x X. + * @param {number} y Y. + * @param {number} x1 X1. + * @param {number} y1 Y1. + * @param {number} x2 X2. + * @param {number} y2 Y2. + * @return {number} Squared distance. + */ +ol.math.squaredSegmentDistance = function(x, y, x1, y1, x2, y2) { + var dx = x2 - x1; + var dy = y2 - y1; + if (dx !== 0 || dy !== 0) { + var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); + if (t > 1) { + x1 = x2; + y1 = y2; + } else if (t > 0) { + x1 += dx * t; + y1 += dy * t; + } + } + return ol.math.squaredDistance(x, y, x1, y1); +}; + + +/** + * Returns the square of the distance between the points (x1, y1) and (x2, y2). + * @param {number} x1 X1. + * @param {number} y1 Y1. + * @param {number} x2 X2. + * @param {number} y2 Y2. + * @return {number} Squared distance. + */ +ol.math.squaredDistance = function(x1, y1, x2, y2) { + var dx = x2 - x1; + var dy = y2 - y1; + return dx * dx + dy * dy; +}; + + +/** + * Solves system of linear equations using Gaussian elimination method. + * + * @param {Array.<Array.<number>>} mat Augmented matrix (n x n + 1 column) + * in row-major order. + * @return {Array.<number>} The resulting vector. + */ +ol.math.solveLinearSystem = function(mat) { + var n = mat.length; + + if (goog.asserts.ENABLE_ASSERTS) { + for (var row = 0; row < n; row++) { + goog.asserts.assert(mat[row].length == n + 1, + 'every row should have correct number of columns'); + } + } + + for (var i = 0; i < n; i++) { + // Find max in the i-th column (ignoring i - 1 first rows) + var maxRow = i; + var maxEl = Math.abs(mat[i][i]); + for (var r = i + 1; r < n; r++) { + var absValue = Math.abs(mat[r][i]); + if (absValue > maxEl) { + maxEl = absValue; + maxRow = r; + } + } + + if (maxEl === 0) { + return null; // matrix is singular + } + + // Swap max row with i-th (current) row + var tmp = mat[maxRow]; + mat[maxRow] = mat[i]; + mat[i] = tmp; + + // Subtract the i-th row to make all the remaining rows 0 in the i-th column + for (var j = i + 1; j < n; j++) { + var coef = -mat[j][i] / mat[i][i]; + for (var k = i; k < n + 1; k++) { + if (i == k) { + mat[j][k] = 0; + } else { + mat[j][k] += coef * mat[i][k]; + } + } + } + } + + // Solve Ax=b for upper triangular matrix A (mat) + var x = new Array(n); + for (var l = n - 1; l >= 0; l--) { + x[l] = mat[l][n] / mat[l][l]; + for (var m = l - 1; m >= 0; m--) { + mat[m][n] -= mat[m][l] * x[l]; + } + } + return x; +}; + + +/** + * Converts radians to to degrees. + * + * @param {number} angleInRadians Angle in radians. + * @return {number} Angle in degrees. + */ +ol.math.toDegrees = function(angleInRadians) { + return angleInRadians * 180 / Math.PI; +}; + + +/** + * Converts degrees to radians. + * + * @param {number} angleInDegrees Angle in degrees. + * @return {number} Angle in radians. + */ +ol.math.toRadians = function(angleInDegrees) { + return angleInDegrees * Math.PI / 180; +}; + +/** + * Returns the modulo of a / b, depending on the sign of b. + * + * @param {number} a Dividend. + * @param {number} b Divisor. + * @return {number} Modulo. + */ +ol.math.modulo = function(a, b) { + var r = a % b; + return r * b < 0 ? r + b : r; +}; + +/** + * Calculates the linearly interpolated value of x between a and b. + * + * @param {number} a Number + * @param {number} b Number + * @param {number} x Value to be interpolated. + * @return {number} Interpolated value. + */ +ol.math.lerp = function(a, b, x) { + return a + x * (b - a); +}; + +goog.provide('ol.CenterConstraint'); + +goog.require('ol.math'); + + +/** + * @param {ol.Extent} extent Extent. + * @return {ol.CenterConstraintType} The constraint. + */ +ol.CenterConstraint.createExtent = function(extent) { + return ( + /** + * @param {ol.Coordinate|undefined} center Center. + * @return {ol.Coordinate|undefined} Center. + */ + function(center) { + if (center) { + return [ + ol.math.clamp(center[0], extent[0], extent[2]), + ol.math.clamp(center[1], extent[1], extent[3]) + ]; + } else { + return undefined; + } + }); +}; + + +/** + * @param {ol.Coordinate|undefined} center Center. + * @return {ol.Coordinate|undefined} Center. + */ +ol.CenterConstraint.none = function(center) { + return center; +}; + +goog.provide('ol.Constraints'); + + +/** + * @constructor + * @param {ol.CenterConstraintType} centerConstraint Center constraint. + * @param {ol.ResolutionConstraintType} resolutionConstraint + * Resolution constraint. + * @param {ol.RotationConstraintType} rotationConstraint + * Rotation constraint. + */ +ol.Constraints = function(centerConstraint, resolutionConstraint, rotationConstraint) { + + /** + * @type {ol.CenterConstraintType} + */ + this.center = centerConstraint; + + /** + * @type {ol.ResolutionConstraintType} + */ + this.resolution = resolutionConstraint; + + /** + * @type {ol.RotationConstraintType} + */ + this.rotation = rotationConstraint; + +}; + +goog.provide('ol.object'); + + +/** + * Polyfill for Object.assign(). Assigns enumerable and own properties from + * one or more source objects to a target object. + * + * @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign + * @param {!Object} target The target object. + * @param {...Object} var_sources The source object(s). + * @return {!Object} The modified target object. + */ +ol.object.assign = (typeof Object.assign === 'function') ? Object.assign : function(target, var_sources) { + if (target === undefined || target === null) { + throw new TypeError('Cannot convert undefined or null to object'); + } + + var output = Object(target); + for (var i = 1, ii = arguments.length; i < ii; ++i) { + var source = arguments[i]; + if (source !== undefined && source !== null) { + for (var key in source) { + if (source.hasOwnProperty(key)) { + output[key] = source[key]; + } + } + } + } + return output; +}; + + +/** + * Removes all properties from an object. + * @param {Object} object The object to clear. + */ +ol.object.clear = function(object) { + for (var property in object) { + delete object[property]; + } +}; + + +/** + * Get an array of property values from an object. + * @param {Object<K,V>} object The object from which to get the values. + * @return {!Array<V>} The property values. + * @template K,V + */ +ol.object.getValues = function(object) { + var values = []; + for (var property in object) { + values.push(object[property]); + } + return values; +}; + + +/** + * Determine if an object has any properties. + * @param {Object} object The object to check. + * @return {boolean} The object is empty. + */ +ol.object.isEmpty = function(object) { + var property; + for (property in object) { + return false; + } + return !property; +}; + +goog.provide('ol.events'); +goog.provide('ol.events.EventType'); +goog.provide('ol.events.KeyCode'); + +goog.require('ol.object'); + + +/** + * @enum {string} + * @const + */ +ol.events.EventType = { + /** + * Generic change event. + * @event ol.events.Event#change + * @api + */ + CHANGE: 'change', + + CLICK: 'click', + DBLCLICK: 'dblclick', + DRAGENTER: 'dragenter', + DRAGOVER: 'dragover', + DROP: 'drop', + ERROR: 'error', + KEYDOWN: 'keydown', + KEYPRESS: 'keypress', + LOAD: 'load', + MOUSEDOWN: 'mousedown', + MOUSEMOVE: 'mousemove', + MOUSEOUT: 'mouseout', + MOUSEUP: 'mouseup', + MOUSEWHEEL: 'mousewheel', + MSPOINTERDOWN: 'mspointerdown', + RESIZE: 'resize', + TOUCHSTART: 'touchstart', + TOUCHMOVE: 'touchmove', + TOUCHEND: 'touchend', + WHEEL: 'wheel' +}; + + +/** + * @enum {number} + * @const + */ +ol.events.KeyCode = { + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40 +}; + + +/** + * Property name on an event target for the listener map associated with the + * event target. + * @const {string} + * @private + */ +ol.events.LISTENER_MAP_PROP_ = 'olm_' + ((Math.random() * 1e4) | 0); + + +/** + * @param {ol.EventsKey} listenerObj Listener object. + * @return {ol.EventsListenerFunctionType} Bound listener. + */ +ol.events.bindListener_ = function(listenerObj) { + var boundListener = function(evt) { + var listener = listenerObj.listener; + var bindTo = listenerObj.bindTo || listenerObj.target; + if (listenerObj.callOnce) { + ol.events.unlistenByKey(listenerObj); + } + return listener.call(bindTo, evt); + }; + listenerObj.boundListener = boundListener; + return boundListener; +}; + + +/** + * Finds the matching {@link ol.EventsKey} in the given listener + * array. + * + * @param {!Array<!ol.EventsKey>} listeners Array of listeners. + * @param {!Function} listener The listener function. + * @param {Object=} opt_this The `this` value inside the listener. + * @param {boolean=} opt_setDeleteIndex Set the deleteIndex on the matching + * listener, for {@link ol.events.unlistenByKey}. + * @return {ol.EventsKey|undefined} The matching listener object. + * @private + */ +ol.events.findListener_ = function(listeners, listener, opt_this, + opt_setDeleteIndex) { + var listenerObj; + for (var i = 0, ii = listeners.length; i < ii; ++i) { + listenerObj = listeners[i]; + if (listenerObj.listener === listener && + listenerObj.bindTo === opt_this) { + if (opt_setDeleteIndex) { + listenerObj.deleteIndex = i; + } + return listenerObj; + } + } + return undefined; +}; + + +/** + * @param {ol.EventTargetLike} target Target. + * @param {string} type Type. + * @return {Array.<ol.EventsKey>|undefined} Listeners. + */ +ol.events.getListeners = function(target, type) { + var listenerMap = target[ol.events.LISTENER_MAP_PROP_]; + return listenerMap ? listenerMap[type] : undefined; +}; + + +/** + * Get the lookup of listeners. If one does not exist on the target, it is + * created. + * @param {ol.EventTargetLike} target Target. + * @return {!Object.<string, Array.<ol.EventsKey>>} Map of + * listeners by event type. + * @private + */ +ol.events.getListenerMap_ = function(target) { + var listenerMap = target[ol.events.LISTENER_MAP_PROP_]; + if (!listenerMap) { + listenerMap = target[ol.events.LISTENER_MAP_PROP_] = {}; + } + return listenerMap; +}; + + +/** + * Clean up all listener objects of the given type. All properties on the + * listener objects will be removed, and if no listeners remain in the listener + * map, it will be removed from the target. + * @param {ol.EventTargetLike} target Target. + * @param {string} type Type. + * @private + */ +ol.events.removeListeners_ = function(target, type) { + var listeners = ol.events.getListeners(target, type); + if (listeners) { + for (var i = 0, ii = listeners.length; i < ii; ++i) { + target.removeEventListener(type, listeners[i].boundListener); + ol.object.clear(listeners[i]); + } + listeners.length = 0; + var listenerMap = target[ol.events.LISTENER_MAP_PROP_]; + if (listenerMap) { + delete listenerMap[type]; + if (Object.keys(listenerMap).length === 0) { + delete target[ol.events.LISTENER_MAP_PROP_]; + } + } + } +}; + + +/** + * Registers an event listener on an event target. Inspired by + * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} + * + * This function efficiently binds a `listener` to a `this` object, and returns + * a key for use with {@link ol.events.unlistenByKey}. + * + * @param {ol.EventTargetLike} target Event target. + * @param {string} type Event type. + * @param {ol.EventsListenerFunctionType} listener Listener. + * @param {Object=} opt_this Object referenced by the `this` keyword in the + * listener. Default is the `target`. + * @param {boolean=} opt_once If true, add the listener as one-off listener. + * @return {ol.EventsKey} Unique key for the listener. + */ +ol.events.listen = function(target, type, listener, opt_this, opt_once) { + var listenerMap = ol.events.getListenerMap_(target); + var listeners = listenerMap[type]; + if (!listeners) { + listeners = listenerMap[type] = []; + } + var listenerObj = ol.events.findListener_(listeners, listener, opt_this, + false); + if (listenerObj) { + if (!opt_once) { + // Turn one-off listener into a permanent one. + listenerObj.callOnce = false; + } + } else { + listenerObj = /** @type {ol.EventsKey} */ ({ + bindTo: opt_this, + callOnce: !!opt_once, + listener: listener, + target: target, + type: type + }); + target.addEventListener(type, ol.events.bindListener_(listenerObj)); + listeners.push(listenerObj); + } + + return listenerObj; +}; + + +/** + * Registers a one-off event listener on an event target. Inspired by + * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} + * + * This function efficiently binds a `listener` as self-unregistering listener + * to a `this` object, and returns a key for use with + * {@link ol.events.unlistenByKey} in case the listener needs to be unregistered + * before it is called. + * + * When {@link ol.events.listen} is called with the same arguments after this + * function, the self-unregistering listener will be turned into a permanent + * listener. + * + * @param {ol.EventTargetLike} target Event target. + * @param {string} type Event type. + * @param {ol.EventsListenerFunctionType} listener Listener. + * @param {Object=} opt_this Object referenced by the `this` keyword in the + * listener. Default is the `target`. + * @return {ol.EventsKey} Key for unlistenByKey. + */ +ol.events.listenOnce = function(target, type, listener, opt_this) { + return ol.events.listen(target, type, listener, opt_this, true); +}; + + +/** + * Unregisters an event listener on an event target. Inspired by + * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} + * + * To return a listener, this function needs to be called with the exact same + * arguments that were used for a previous {@link ol.events.listen} call. + * + * @param {ol.EventTargetLike} target Event target. + * @param {string} type Event type. + * @param {ol.EventsListenerFunctionType} listener Listener. + * @param {Object=} opt_this Object referenced by the `this` keyword in the + * listener. Default is the `target`. + */ +ol.events.unlisten = function(target, type, listener, opt_this) { + var listeners = ol.events.getListeners(target, type); + if (listeners) { + var listenerObj = ol.events.findListener_(listeners, listener, opt_this, + true); + if (listenerObj) { + ol.events.unlistenByKey(listenerObj); + } + } +}; + + +/** + * Unregisters event listeners on an event target. Inspired by + * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} + * + * The argument passed to this function is the key returned from + * {@link ol.events.listen} or {@link ol.events.listenOnce}. + * + * @param {ol.EventsKey} key The key. + */ +ol.events.unlistenByKey = function(key) { + if (key && key.target) { + key.target.removeEventListener(key.type, key.boundListener); + var listeners = ol.events.getListeners(key.target, key.type); + if (listeners) { + var i = 'deleteIndex' in key ? key.deleteIndex : listeners.indexOf(key); + if (i !== -1) { + listeners.splice(i, 1); + } + if (listeners.length === 0) { + ol.events.removeListeners_(key.target, key.type); + } + } + ol.object.clear(key); + } +}; + + +/** + * Unregisters all event listeners on an event target. Inspired by + * {@link https://google.github.io/closure-library/api/source/closure/goog/events/events.js.src.html} + * + * @param {ol.EventTargetLike} target Target. + */ +ol.events.unlistenAll = function(target) { + var listenerMap = ol.events.getListenerMap_(target); + for (var type in listenerMap) { + ol.events.removeListeners_(target, type); + } +}; + +goog.provide('ol.Disposable'); + +goog.require('ol'); + +/** + * Objects that need to clean up after themselves. + * @constructor + */ +ol.Disposable = function() {}; + +/** + * The object has already been disposed. + * @type {boolean} + * @private + */ +ol.Disposable.prototype.disposed_ = false; + +/** + * Clean up. + */ +ol.Disposable.prototype.dispose = function() { + if (!this.disposed_) { + this.disposed_ = true; + this.disposeInternal(); + } +}; + +/** + * Extension point for disposable objects. + * @protected + */ +ol.Disposable.prototype.disposeInternal = ol.nullFunction; + +goog.provide('ol.events.Event'); + + +/** + * @classdesc + * Stripped down implementation of the W3C DOM Level 2 Event interface. + * @see {@link https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-interface} + * + * This implementation only provides `type` and `target` properties, and + * `stopPropagation` and `preventDefault` methods. It is meant as base class + * for higher level events defined in the library, and works with + * {@link ol.events.EventTarget}. + * + * @constructor + * @implements {oli.events.Event} + * @param {string} type Type. + * @param {Object=} opt_target Target. + */ +ol.events.Event = function(type, opt_target) { + + /** + * @type {boolean} + */ + this.propagationStopped; + + /** + * The event type. + * @type {string} + * @api stable + */ + this.type = type; + + /** + * The event target. + * @type {Object} + * @api stable + */ + this.target = opt_target || null; + +}; + + +/** + * Stop event propagation. + * @function + * @api stable + */ +ol.events.Event.prototype.preventDefault = + +/** + * Stop event propagation. + * @function + * @api stable + */ +ol.events.Event.prototype.stopPropagation = function() { + this.propagationStopped = true; +}; + + +/** + * @param {Event|ol.events.Event} evt Event + */ +ol.events.Event.stopPropagation = function(evt) { + evt.stopPropagation(); +}; + + +/** + * @param {Event|ol.events.Event} evt Event + */ +ol.events.Event.preventDefault = function(evt) { + evt.preventDefault(); +}; + +goog.provide('ol.events.EventTarget'); + +goog.require('goog.asserts'); +goog.require('ol.Disposable'); +goog.require('ol.events'); +goog.require('ol.events.Event'); + + +/** + * @classdesc + * A simplified implementation of the W3C DOM Level 2 EventTarget interface. + * @see {@link https://www.w3.org/TR/2000/REC-DOM-Level-2-Events-20001113/events.html#Events-EventTarget} + * + * There are two important simplifications compared to the specification: + * + * 1. The handling of `useCapture` in `addEventListener` and + * `removeEventListener`. There is no real capture model. + * 2. The handling of `stopPropagation` and `preventDefault` on `dispatchEvent`. + * There is no event target hierarchy. When a listener calls + * `stopPropagation` or `preventDefault` on an event object, it means that no + * more listeners after this one will be called. Same as when the listener + * returns false. + * + * @constructor + * @extends {ol.Disposable} + */ +ol.events.EventTarget = function() { + + ol.Disposable.call(this); + + /** + * @private + * @type {!Object.<string, number>} + */ + this.pendingRemovals_ = {}; + + /** + * @private + * @type {!Object.<string, number>} + */ + this.dispatching_ = {}; + + /** + * @private + * @type {!Object.<string, Array.<ol.EventsListenerFunctionType>>} + */ + this.listeners_ = {}; + +}; +ol.inherits(ol.events.EventTarget, ol.Disposable); + + +/** + * @param {string} type Type. + * @param {ol.EventsListenerFunctionType} listener Listener. + */ +ol.events.EventTarget.prototype.addEventListener = function(type, listener) { + var listeners = this.listeners_[type]; + if (!listeners) { + listeners = this.listeners_[type] = []; + } + if (listeners.indexOf(listener) === -1) { + listeners.push(listener); + } +}; + + +/** + * @param {{type: string, + * target: (EventTarget|ol.events.EventTarget|undefined)}|ol.events.Event| + * string} event Event or event type. + * @return {boolean|undefined} `false` if anyone called preventDefault on the + * event object or if any of the listeners returned false. + */ +ol.events.EventTarget.prototype.dispatchEvent = function(event) { + var evt = typeof event === 'string' ? new ol.events.Event(event) : event; + var type = evt.type; + evt.target = this; + var listeners = this.listeners_[type]; + var propagate; + if (listeners) { + if (!(type in this.dispatching_)) { + this.dispatching_[type] = 0; + this.pendingRemovals_[type] = 0; + } + ++this.dispatching_[type]; + for (var i = 0, ii = listeners.length; i < ii; ++i) { + if (listeners[i].call(this, evt) === false || evt.propagationStopped) { + propagate = false; + break; + } + } + --this.dispatching_[type]; + if (this.dispatching_[type] === 0) { + var pendingRemovals = this.pendingRemovals_[type]; + delete this.pendingRemovals_[type]; + while (pendingRemovals--) { + this.removeEventListener(type, ol.nullFunction); + } + delete this.dispatching_[type]; + } + return propagate; + } +}; + + +/** + * @inheritDoc + */ +ol.events.EventTarget.prototype.disposeInternal = function() { + ol.events.unlistenAll(this); +}; + + +/** + * Get the listeners for a specified event type. Listeners are returned in the + * order that they will be called in. + * + * @param {string} type Type. + * @return {Array.<ol.EventsListenerFunctionType>} Listeners. + */ +ol.events.EventTarget.prototype.getListeners = function(type) { + return this.listeners_[type]; +}; + + +/** + * @param {string=} opt_type Type. If not provided, + * `true` will be returned if this EventTarget has any listeners. + * @return {boolean} Has listeners. + */ +ol.events.EventTarget.prototype.hasListener = function(opt_type) { + return opt_type ? + opt_type in this.listeners_ : + Object.keys(this.listeners_).length > 0; +}; + + +/** + * @param {string} type Type. + * @param {ol.EventsListenerFunctionType} listener Listener. + */ +ol.events.EventTarget.prototype.removeEventListener = function(type, listener) { + var listeners = this.listeners_[type]; + if (listeners) { + var index = listeners.indexOf(listener); + goog.asserts.assert(index != -1, 'listener not found'); + if (type in this.pendingRemovals_) { + // make listener a no-op, and remove later in #dispatchEvent() + listeners[index] = ol.nullFunction; + ++this.pendingRemovals_[type]; + } else { + listeners.splice(index, 1); + if (listeners.length === 0) { + delete this.listeners_[type]; + } + } + } +}; + +goog.provide('ol.Observable'); + +goog.require('ol.events'); +goog.require('ol.events.EventTarget'); +goog.require('ol.events.EventType'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * An event target providing convenient methods for listener registration + * and unregistration. A generic `change` event is always available through + * {@link ol.Observable#changed}. + * + * @constructor + * @extends {ol.events.EventTarget} + * @fires change + * @struct + * @api stable + */ +ol.Observable = function() { + + ol.events.EventTarget.call(this); + + /** + * @private + * @type {number} + */ + this.revision_ = 0; + +}; +ol.inherits(ol.Observable, ol.events.EventTarget); + + +/** + * Removes an event listener using the key returned by `on()` or `once()`. + * @param {ol.EventsKey|Array.<ol.EventsKey>} key The key returned by `on()` + * or `once()` (or an array of keys). + * @api stable + */ +ol.Observable.unByKey = function(key) { + if (Array.isArray(key)) { + for (var i = 0, ii = key.length; i < ii; ++i) { + ol.events.unlistenByKey(key[i]); + } + } else { + ol.events.unlistenByKey(/** @type {ol.EventsKey} */ (key)); + } +}; + + +/** + * Increases the revision counter and dispatches a 'change' event. + * @api + */ +ol.Observable.prototype.changed = function() { + ++this.revision_; + this.dispatchEvent(ol.events.EventType.CHANGE); +}; + + +/** + * Triggered when the revision counter is increased. + * @event change + * @api + */ + + +/** + * Dispatches an event and calls all listeners listening for events + * of this type. The event parameter can either be a string or an + * Object with a `type` property. + * + * @param {{type: string, + * target: (EventTarget|ol.events.EventTarget|undefined)}|ol.events.Event| + * string} event Event object. + * @function + * @api + */ +ol.Observable.prototype.dispatchEvent; + + +/** + * Get the version number for this object. Each time the object is modified, + * its version number will be incremented. + * @return {number} Revision. + * @api + */ +ol.Observable.prototype.getRevision = function() { + return this.revision_; +}; + + +/** + * Listen for a certain type of event. + * @param {string|Array.<string>} type The event type or array of event types. + * @param {function(?): ?} listener The listener function. + * @param {Object=} opt_this The object to use as `this` in `listener`. + * @return {ol.EventsKey|Array.<ol.EventsKey>} Unique key for the listener. If + * called with an array of event types as the first argument, the return + * will be an array of keys. + * @api stable + */ +ol.Observable.prototype.on = function(type, listener, opt_this) { + if (Array.isArray(type)) { + var len = type.length; + var keys = new Array(len); + for (var i = 0; i < len; ++i) { + keys[i] = ol.events.listen(this, type[i], listener, opt_this); + } + return keys; + } else { + return ol.events.listen( + this, /** @type {string} */ (type), listener, opt_this); + } +}; + + +/** + * Listen once for a certain type of event. + * @param {string|Array.<string>} type The event type or array of event types. + * @param {function(?): ?} listener The listener function. + * @param {Object=} opt_this The object to use as `this` in `listener`. + * @return {ol.EventsKey|Array.<ol.EventsKey>} Unique key for the listener. If + * called with an array of event types as the first argument, the return + * will be an array of keys. + * @api stable + */ +ol.Observable.prototype.once = function(type, listener, opt_this) { + if (Array.isArray(type)) { + var len = type.length; + var keys = new Array(len); + for (var i = 0; i < len; ++i) { + keys[i] = ol.events.listenOnce(this, type[i], listener, opt_this); + } + return keys; + } else { + return ol.events.listenOnce( + this, /** @type {string} */ (type), listener, opt_this); + } +}; + + +/** + * Unlisten for a certain type of event. + * @param {string|Array.<string>} type The event type or array of event types. + * @param {function(?): ?} listener The listener function. + * @param {Object=} opt_this The object which was used as `this` by the + * `listener`. + * @api stable + */ +ol.Observable.prototype.un = function(type, listener, opt_this) { + if (Array.isArray(type)) { + for (var i = 0, ii = type.length; i < ii; ++i) { + ol.events.unlisten(this, type[i], listener, opt_this); + } + return; + } else { + ol.events.unlisten(this, /** @type {string} */ (type), listener, opt_this); + } +}; + + +/** + * Removes an event listener using the key returned by `on()` or `once()`. + * Note that using the {@link ol.Observable.unByKey} static function is to + * be preferred. + * @param {ol.EventsKey|Array.<ol.EventsKey>} key The key returned by `on()` + * or `once()` (or an array of keys). + * @function + * @api stable + */ +ol.Observable.prototype.unByKey = ol.Observable.unByKey; + +goog.provide('ol.Object'); +goog.provide('ol.ObjectEvent'); +goog.provide('ol.ObjectEventType'); + +goog.require('ol.Observable'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.object'); + + +/** + * @enum {string} + */ +ol.ObjectEventType = { + /** + * Triggered when a property is changed. + * @event ol.ObjectEvent#propertychange + * @api stable + */ + PROPERTYCHANGE: 'propertychange' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.Object} instances are instances of this type. + * + * @param {string} type The event type. + * @param {string} key The property name. + * @param {*} oldValue The old value for `key`. + * @extends {ol.events.Event} + * @implements {oli.ObjectEvent} + * @constructor + */ +ol.ObjectEvent = function(type, key, oldValue) { + ol.events.Event.call(this, type); + + /** + * The name of the property whose value is changing. + * @type {string} + * @api stable + */ + this.key = key; + + /** + * The old value. To get the new value use `e.target.get(e.key)` where + * `e` is the event object. + * @type {*} + * @api stable + */ + this.oldValue = oldValue; + +}; +ol.inherits(ol.ObjectEvent, ol.events.Event); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Most non-trivial classes inherit from this. + * + * This extends {@link ol.Observable} with observable properties, where each + * property is observable as well as the object as a whole. + * + * Classes that inherit from this have pre-defined properties, to which you can + * add your owns. The pre-defined properties are listed in this documentation as + * 'Observable Properties', and have their own accessors; for example, + * {@link ol.Map} has a `target` property, accessed with `getTarget()` and + * changed with `setTarget()`. Not all properties are however settable. There + * are also general-purpose accessors `get()` and `set()`. For example, + * `get('target')` is equivalent to `getTarget()`. + * + * The `set` accessors trigger a change event, and you can monitor this by + * registering a listener. For example, {@link ol.View} has a `center` + * property, so `view.on('change:center', function(evt) {...});` would call the + * function whenever the value of the center property changes. Within the + * function, `evt.target` would be the view, so `evt.target.getCenter()` would + * return the new center. + * + * You can add your own observable properties with + * `object.set('prop', 'value')`, and retrieve that with `object.get('prop')`. + * You can listen for changes on that property value with + * `object.on('change:prop', listener)`. You can get a list of all + * properties with {@link ol.Object#getProperties object.getProperties()}. + * + * Note that the observable properties are separate from standard JS properties. + * You can, for example, give your map object a title with + * `map.title='New title'` and with `map.set('title', 'Another title')`. The + * first will be a `hasOwnProperty`; the second will appear in + * `getProperties()`. Only the second is observable. + * + * Properties can be deleted by using the unset method. E.g. + * object.unset('foo'). + * + * @constructor + * @extends {ol.Observable} + * @param {Object.<string, *>=} opt_values An object with key-value pairs. + * @fires ol.ObjectEvent + * @api + */ +ol.Object = function(opt_values) { + ol.Observable.call(this); + + // Call goog.getUid to ensure that the order of objects' ids is the same as + // the order in which they were created. This also helps to ensure that + // object properties are always added in the same order, which helps many + // JavaScript engines generate faster code. + goog.getUid(this); + + /** + * @private + * @type {!Object.<string, *>} + */ + this.values_ = {}; + + if (opt_values !== undefined) { + this.setProperties(opt_values); + } +}; +ol.inherits(ol.Object, ol.Observable); + + +/** + * @private + * @type {Object.<string, string>} + */ +ol.Object.changeEventTypeCache_ = {}; + + +/** + * @param {string} key Key name. + * @return {string} Change name. + */ +ol.Object.getChangeEventType = function(key) { + return ol.Object.changeEventTypeCache_.hasOwnProperty(key) ? + ol.Object.changeEventTypeCache_[key] : + (ol.Object.changeEventTypeCache_[key] = 'change:' + key); +}; + + +/** + * Gets a value. + * @param {string} key Key name. + * @return {*} Value. + * @api stable + */ +ol.Object.prototype.get = function(key) { + var value; + if (this.values_.hasOwnProperty(key)) { + value = this.values_[key]; + } + return value; +}; + + +/** + * Get a list of object property names. + * @return {Array.<string>} List of property names. + * @api stable + */ +ol.Object.prototype.getKeys = function() { + return Object.keys(this.values_); +}; + + +/** + * Get an object of all property names and values. + * @return {Object.<string, *>} Object. + * @api stable + */ +ol.Object.prototype.getProperties = function() { + return ol.object.assign({}, this.values_); +}; + + +/** + * @param {string} key Key name. + * @param {*} oldValue Old value. + */ +ol.Object.prototype.notify = function(key, oldValue) { + var eventType; + eventType = ol.Object.getChangeEventType(key); + this.dispatchEvent(new ol.ObjectEvent(eventType, key, oldValue)); + eventType = ol.ObjectEventType.PROPERTYCHANGE; + this.dispatchEvent(new ol.ObjectEvent(eventType, key, oldValue)); +}; + + +/** + * Sets a value. + * @param {string} key Key name. + * @param {*} value Value. + * @param {boolean=} opt_silent Update without triggering an event. + * @api stable + */ +ol.Object.prototype.set = function(key, value, opt_silent) { + if (opt_silent) { + this.values_[key] = value; + } else { + var oldValue = this.values_[key]; + this.values_[key] = value; + if (oldValue !== value) { + this.notify(key, oldValue); + } + } +}; + + +/** + * Sets a collection of key-value pairs. Note that this changes any existing + * properties and adds new ones (it does not remove any existing properties). + * @param {Object.<string, *>} values Values. + * @param {boolean=} opt_silent Update without triggering an event. + * @api stable + */ +ol.Object.prototype.setProperties = function(values, opt_silent) { + var key; + for (key in values) { + this.set(key, values[key], opt_silent); + } +}; + + +/** + * Unsets a property. + * @param {string} key Key name. + * @param {boolean=} opt_silent Unset without triggering an event. + * @api stable + */ +ol.Object.prototype.unset = function(key, opt_silent) { + if (key in this.values_) { + var oldValue = this.values_[key]; + delete this.values_[key]; + if (!opt_silent) { + this.notify(key, oldValue); + } + } +}; + +goog.provide('ol.array'); + +goog.require('goog.asserts'); + + +/** + * Performs a binary search on the provided sorted list and returns the index of the item if found. If it can't be found it'll return -1. + * https://github.com/darkskyapp/binary-search + * + * @param {Array.<*>} haystack Items to search through. + * @param {*} needle The item to look for. + * @param {Function=} opt_comparator Comparator function. + * @return {number} The index of the item if found, -1 if not. + */ +ol.array.binarySearch = function(haystack, needle, opt_comparator) { + var mid, cmp; + var comparator = opt_comparator || ol.array.numberSafeCompareFunction; + var low = 0; + var high = haystack.length; + var found = false; + + while (low < high) { + /* Note that "(low + high) >>> 1" may overflow, and results in a typecast + * to double (which gives the wrong results). */ + mid = low + (high - low >> 1); + cmp = +comparator(haystack[mid], needle); + + if (cmp < 0.0) { /* Too low. */ + low = mid + 1; + + } else { /* Key found or too high */ + high = mid; + found = !cmp; + } + } + + /* Key not found. */ + return found ? low : ~low; +}; + +/** + * @param {Array.<number>} arr Array. + * @param {number} target Target. + * @return {number} Index. + */ +ol.array.binaryFindNearest = function(arr, target) { + var index = ol.array.binarySearch(arr, target, + /** + * @param {number} a A. + * @param {number} b B. + * @return {number} b minus a. + */ + function(a, b) { + return b - a; + }); + if (index >= 0) { + return index; + } else if (index == -1) { + return 0; + } else if (index == -arr.length - 1) { + return arr.length - 1; + } else { + var left = -index - 2; + var right = -index - 1; + if (arr[left] - target < target - arr[right]) { + return left; + } else { + return right; + } + } +}; + + +/** + * Compare function for array sort that is safe for numbers. + * @param {*} a The first object to be compared. + * @param {*} b The second object to be compared. + * @return {number} A negative number, zero, or a positive number as the first + * argument is less than, equal to, or greater than the second. + */ +ol.array.numberSafeCompareFunction = function(a, b) { + return a > b ? 1 : a < b ? -1 : 0; +}; + + +/** + * Whether the array contains the given object. + * @param {Array.<*>} arr The array to test for the presence of the element. + * @param {*} obj The object for which to test. + * @return {boolean} The object is in the array. + */ +ol.array.includes = function(arr, obj) { + return arr.indexOf(obj) >= 0; +}; + + +/** + * @param {Array.<number>} arr Array. + * @param {number} target Target. + * @param {number} direction 0 means return the nearest, > 0 + * means return the largest nearest, < 0 means return the + * smallest nearest. + * @return {number} Index. + */ +ol.array.linearFindNearest = function(arr, target, direction) { + var n = arr.length; + if (arr[0] <= target) { + return 0; + } else if (target <= arr[n - 1]) { + return n - 1; + } else { + var i; + if (direction > 0) { + for (i = 1; i < n; ++i) { + if (arr[i] < target) { + return i - 1; + } + } + } else if (direction < 0) { + for (i = 1; i < n; ++i) { + if (arr[i] <= target) { + return i; + } + } + } else { + for (i = 1; i < n; ++i) { + if (arr[i] == target) { + return i; + } else if (arr[i] < target) { + if (arr[i - 1] - target < target - arr[i]) { + return i - 1; + } else { + return i; + } + } + } + } + // We should never get here, but the compiler complains + // if it finds a path for which no number is returned. + goog.asserts.fail(); + return n - 1; + } +}; + + +/** + * @param {Array.<*>} arr Array. + * @param {number} begin Begin index. + * @param {number} end End index. + */ +ol.array.reverseSubArray = function(arr, begin, end) { + goog.asserts.assert(begin >= 0, + 'Array begin index should be equal to or greater than 0'); + goog.asserts.assert(end < arr.length, + 'Array end index should be less than the array length'); + while (begin < end) { + var tmp = arr[begin]; + arr[begin] = arr[end]; + arr[end] = tmp; + ++begin; + --end; + } +}; + + +/** + * @param {Array.<*>} arr Array. + * @return {!Array.<?>} Flattened Array. + */ +ol.array.flatten = function(arr) { + var data = arr.reduce(function(flattened, value) { + if (Array.isArray(value)) { + return flattened.concat(ol.array.flatten(value)); + } else { + return flattened.concat(value); + } + }, []); + return data; +}; + + +/** + * @param {Array.<VALUE>} arr The array to modify. + * @param {Array.<VALUE>|VALUE} data The elements or arrays of elements + * to add to arr. + * @template VALUE + */ +ol.array.extend = function(arr, data) { + var i; + var extension = goog.isArrayLike(data) ? data : [data]; + var length = extension.length; + for (i = 0; i < length; i++) { + arr[arr.length] = extension[i]; + } +}; + + +/** + * @param {Array.<VALUE>} arr The array to modify. + * @param {VALUE} obj The element to remove. + * @template VALUE + * @return {boolean} If the element was removed. + */ +ol.array.remove = function(arr, obj) { + var i = arr.indexOf(obj); + var found = i > -1; + if (found) { + arr.splice(i, 1); + } + return found; +}; + + +/** + * @param {Array.<VALUE>} arr The array to search in. + * @param {function(VALUE, number, ?) : boolean} func The function to compare. + * @template VALUE + * @return {VALUE} The element found. + */ +ol.array.find = function(arr, func) { + var length = arr.length >>> 0; + var value; + + for (var i = 0; i < length; i++) { + value = arr[i]; + if (func(value, i, arr)) { + return value; + } + } + return null; +}; + + +/** + * @param {Array|Uint8ClampedArray} arr1 The first array to compare. + * @param {Array|Uint8ClampedArray} arr2 The second array to compare. + * @return {boolean} Whether the two arrays are equal. + */ +ol.array.equals = function(arr1, arr2) { + var len1 = arr1.length; + if (len1 !== arr2.length) { + return false; + } + for (var i = 0; i < len1; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + return true; +}; + + +/** + * @param {Array.<*>} arr The array to sort (modifies original). + * @param {Function} compareFnc Comparison function. + */ +ol.array.stableSort = function(arr, compareFnc) { + var length = arr.length; + var tmp = Array(arr.length); + var i; + for (i = 0; i < length; i++) { + tmp[i] = {index: i, value: arr[i]}; + } + tmp.sort(function(a, b) { + return compareFnc(a.value, b.value) || a.index - b.index; + }); + for (i = 0; i < arr.length; i++) { + arr[i] = tmp[i].value; + } +}; + + +/** + * @param {Array.<*>} arr The array to search in. + * @param {Function} func Comparison function. + * @return {number} Return index. + */ +ol.array.findIndex = function(arr, func) { + var index; + var found = !arr.every(function(el, idx) { + index = idx; + return !func(el, idx, arr); + }); + return found ? index : -1; +}; + + +/** + * @param {Array.<*>} arr The array to test. + * @param {Function=} opt_func Comparison function. + * @param {boolean=} opt_strict Strictly sorted (default false). + * @return {boolean} Return index. + */ +ol.array.isSorted = function(arr, opt_func, opt_strict) { + var compare = opt_func || ol.array.numberSafeCompareFunction; + return arr.every(function(currentVal, index) { + if (index === 0) { + return true; + } + var res = compare(arr[index - 1], currentVal); + return !(res > 0 || opt_strict && res === 0); + }); +}; + +goog.provide('ol.ResolutionConstraint'); + +goog.require('ol.array'); +goog.require('ol.math'); + + +/** + * @param {Array.<number>} resolutions Resolutions. + * @return {ol.ResolutionConstraintType} Zoom function. + */ +ol.ResolutionConstraint.createSnapToResolutions = function(resolutions) { + return ( + /** + * @param {number|undefined} resolution Resolution. + * @param {number} delta Delta. + * @param {number} direction Direction. + * @return {number|undefined} Resolution. + */ + function(resolution, delta, direction) { + if (resolution !== undefined) { + var z = + ol.array.linearFindNearest(resolutions, resolution, direction); + z = ol.math.clamp(z + delta, 0, resolutions.length - 1); + return resolutions[z]; + } else { + return undefined; + } + }); +}; + + +/** + * @param {number} power Power. + * @param {number} maxResolution Maximum resolution. + * @param {number=} opt_maxLevel Maximum level. + * @return {ol.ResolutionConstraintType} Zoom function. + */ +ol.ResolutionConstraint.createSnapToPower = function(power, maxResolution, opt_maxLevel) { + return ( + /** + * @param {number|undefined} resolution Resolution. + * @param {number} delta Delta. + * @param {number} direction Direction. + * @return {number|undefined} Resolution. + */ + function(resolution, delta, direction) { + if (resolution !== undefined) { + var offset; + if (direction > 0) { + offset = 0; + } else if (direction < 0) { + offset = 1; + } else { + offset = 0.5; + } + var oldLevel = Math.floor( + Math.log(maxResolution / resolution) / Math.log(power) + offset); + var newLevel = Math.max(oldLevel + delta, 0); + if (opt_maxLevel !== undefined) { + newLevel = Math.min(newLevel, opt_maxLevel); + } + return maxResolution / Math.pow(power, newLevel); + } else { + return undefined; + } + }); +}; + +goog.provide('ol.RotationConstraint'); + +goog.require('ol.math'); + + +/** + * @param {number|undefined} rotation Rotation. + * @param {number} delta Delta. + * @return {number|undefined} Rotation. + */ +ol.RotationConstraint.disable = function(rotation, delta) { + if (rotation !== undefined) { + return 0; + } else { + return undefined; + } +}; + + +/** + * @param {number|undefined} rotation Rotation. + * @param {number} delta Delta. + * @return {number|undefined} Rotation. + */ +ol.RotationConstraint.none = function(rotation, delta) { + if (rotation !== undefined) { + return rotation + delta; + } else { + return undefined; + } +}; + + +/** + * @param {number} n N. + * @return {ol.RotationConstraintType} Rotation constraint. + */ +ol.RotationConstraint.createSnapToN = function(n) { + var theta = 2 * Math.PI / n; + return ( + /** + * @param {number|undefined} rotation Rotation. + * @param {number} delta Delta. + * @return {number|undefined} Rotation. + */ + function(rotation, delta) { + if (rotation !== undefined) { + rotation = Math.floor((rotation + delta) / theta + 0.5) * theta; + return rotation; + } else { + return undefined; + } + }); +}; + + +/** + * @param {number=} opt_tolerance Tolerance. + * @return {ol.RotationConstraintType} Rotation constraint. + */ +ol.RotationConstraint.createSnapToZero = function(opt_tolerance) { + var tolerance = opt_tolerance || ol.math.toRadians(5); + return ( + /** + * @param {number|undefined} rotation Rotation. + * @param {number} delta Delta. + * @return {number|undefined} Rotation. + */ + function(rotation, delta) { + if (rotation !== undefined) { + if (Math.abs(rotation + delta) <= tolerance) { + return 0; + } else { + return rotation + delta; + } + } else { + return undefined; + } + }); +}; + +goog.provide('ol.string'); + +/** + * @param {number} number Number to be formatted + * @param {number} width The desired width + * @param {number=} opt_precision Precision of the output string (i.e. number of decimal places) + * @returns {string} Formatted string +*/ +ol.string.padNumber = function(number, width, opt_precision) { + var numberString = opt_precision !== undefined ? number.toFixed(opt_precision) : '' + number; + var decimal = numberString.indexOf('.'); + decimal = decimal === -1 ? numberString.length : decimal; + return decimal > width ? numberString : new Array(1 + width - decimal).join('0') + numberString; +}; + +/** + * Adapted from https://github.com/omichelsen/compare-versions/blob/master/index.js + * @param {string|number} v1 First version + * @param {string|number} v2 Second version + * @returns {number} Value + */ +ol.string.compareVersions = function(v1, v2) { + var s1 = ('' + v1).split('.'); + var s2 = ('' + v2).split('.'); + + for (var i = 0; i < Math.max(s1.length, s2.length); i++) { + var n1 = parseInt(s1[i] || '0', 10); + var n2 = parseInt(s2[i] || '0', 10); + + if (n1 > n2) return 1; + if (n2 > n1) return -1; + } + + return 0; +}; + +goog.provide('ol.coordinate'); + +goog.require('ol.math'); +goog.require('ol.string'); + + +/** + * Add `delta` to `coordinate`. `coordinate` is modified in place and returned + * by the function. + * + * Example: + * + * var coord = [7.85, 47.983333]; + * ol.coordinate.add(coord, [-2, 4]); + * // coord is now [5.85, 51.983333] + * + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.Coordinate} delta Delta. + * @return {ol.Coordinate} The input coordinate adjusted by the given delta. + * @api stable + */ +ol.coordinate.add = function(coordinate, delta) { + coordinate[0] += delta[0]; + coordinate[1] += delta[1]; + return coordinate; +}; + + +/** + * Calculates the point closest to the passed coordinate on the passed segment. + * This is the foot of the perpendicular of the coordinate to the segment when + * the foot is on the segment, or the closest segment coordinate when the foot + * is outside the segment. + * + * @param {ol.Coordinate} coordinate The coordinate. + * @param {Array.<ol.Coordinate>} segment The two coordinates of the segment. + * @return {ol.Coordinate} The foot of the perpendicular of the coordinate to + * the segment. + */ +ol.coordinate.closestOnSegment = function(coordinate, segment) { + var x0 = coordinate[0]; + var y0 = coordinate[1]; + var start = segment[0]; + var end = segment[1]; + var x1 = start[0]; + var y1 = start[1]; + var x2 = end[0]; + var y2 = end[1]; + var dx = x2 - x1; + var dy = y2 - y1; + var along = (dx === 0 && dy === 0) ? 0 : + ((dx * (x0 - x1)) + (dy * (y0 - y1))) / ((dx * dx + dy * dy) || 0); + var x, y; + if (along <= 0) { + x = x1; + y = y1; + } else if (along >= 1) { + x = x2; + y = y2; + } else { + x = x1 + along * dx; + y = y1 + along * dy; + } + return [x, y]; +}; + + +/** + * Returns a {@link ol.CoordinateFormatType} function that can be used to format + * a {ol.Coordinate} to a string. + * + * Example without specifying the fractional digits: + * + * var coord = [7.85, 47.983333]; + * var stringifyFunc = ol.coordinate.createStringXY(); + * var out = stringifyFunc(coord); + * // out is now '8, 48' + * + * Example with explicitly specifying 2 fractional digits: + * + * var coord = [7.85, 47.983333]; + * var stringifyFunc = ol.coordinate.createStringXY(2); + * var out = stringifyFunc(coord); + * // out is now '7.85, 47.98' + * + * @param {number=} opt_fractionDigits The number of digits to include + * after the decimal point. Default is `0`. + * @return {ol.CoordinateFormatType} Coordinate format. + * @api stable + */ +ol.coordinate.createStringXY = function(opt_fractionDigits) { + return ( + /** + * @param {ol.Coordinate|undefined} coordinate Coordinate. + * @return {string} String XY. + */ + function(coordinate) { + return ol.coordinate.toStringXY(coordinate, opt_fractionDigits); + }); +}; + + +/** + * @private + * @param {number} degrees Degrees. + * @param {string} hemispheres Hemispheres. + * @param {number=} opt_fractionDigits The number of digits to include + * after the decimal point. Default is `0`. + * @return {string} String. + */ +ol.coordinate.degreesToStringHDMS_ = function(degrees, hemispheres, opt_fractionDigits) { + var normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180; + var x = Math.abs(3600 * normalizedDegrees); + var dflPrecision = opt_fractionDigits || 0; + return Math.floor(x / 3600) + '\u00b0 ' + + ol.string.padNumber(Math.floor((x / 60) % 60), 2) + '\u2032 ' + + ol.string.padNumber((x % 60), 2, dflPrecision) + '\u2033 ' + + hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0); +}; + + +/** + * Transforms the given {@link ol.Coordinate} to a string using the given string + * template. The strings `{x}` and `{y}` in the template will be replaced with + * the first and second coordinate values respectively. + * + * Example without specifying the fractional digits: + * + * var coord = [7.85, 47.983333]; + * var template = 'Coordinate is ({x}|{y}).'; + * var out = ol.coordinate.format(coord, template); + * // out is now 'Coordinate is (8|48).' + * + * Example explicitly specifying the fractional digits: + * + * var coord = [7.85, 47.983333]; + * var template = 'Coordinate is ({x}|{y}).'; + * var out = ol.coordinate.format(coord, template, 2); + * // out is now 'Coordinate is (7.85|47.98).' + * + * @param {ol.Coordinate|undefined} coordinate Coordinate. + * @param {string} template A template string with `{x}` and `{y}` placeholders + * that will be replaced by first and second coordinate values. + * @param {number=} opt_fractionDigits The number of digits to include + * after the decimal point. Default is `0`. + * @return {string} Formatted coordinate. + * @api stable + */ +ol.coordinate.format = function(coordinate, template, opt_fractionDigits) { + if (coordinate) { + return template + .replace('{x}', coordinate[0].toFixed(opt_fractionDigits)) + .replace('{y}', coordinate[1].toFixed(opt_fractionDigits)); + } else { + return ''; + } +}; + + +/** + * @param {ol.Coordinate} coordinate1 First coordinate. + * @param {ol.Coordinate} coordinate2 Second coordinate. + * @return {boolean} Whether the passed coordinates are equal. + */ +ol.coordinate.equals = function(coordinate1, coordinate2) { + var equals = true; + for (var i = coordinate1.length - 1; i >= 0; --i) { + if (coordinate1[i] != coordinate2[i]) { + equals = false; + break; + } + } + return equals; +}; + + +/** + * Rotate `coordinate` by `angle`. `coordinate` is modified in place and + * returned by the function. + * + * Example: + * + * var coord = [7.85, 47.983333]; + * var rotateRadians = Math.PI / 2; // 90 degrees + * ol.coordinate.rotate(coord, rotateRadians); + * // coord is now [-47.983333, 7.85] + * + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} angle Angle in radian. + * @return {ol.Coordinate} Coordinate. + * @api stable + */ +ol.coordinate.rotate = function(coordinate, angle) { + var cosAngle = Math.cos(angle); + var sinAngle = Math.sin(angle); + var x = coordinate[0] * cosAngle - coordinate[1] * sinAngle; + var y = coordinate[1] * cosAngle + coordinate[0] * sinAngle; + coordinate[0] = x; + coordinate[1] = y; + return coordinate; +}; + + +/** + * Scale `coordinate` by `scale`. `coordinate` is modified in place and returned + * by the function. + * + * Example: + * + * var coord = [7.85, 47.983333]; + * var scale = 1.2; + * ol.coordinate.scale(coord, scale); + * // coord is now [9.42, 57.5799996] + * + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} scale Scale factor. + * @return {ol.Coordinate} Coordinate. + */ +ol.coordinate.scale = function(coordinate, scale) { + coordinate[0] *= scale; + coordinate[1] *= scale; + return coordinate; +}; + + +/** + * Subtract `delta` to `coordinate`. `coordinate` is modified in place and + * returned by the function. + * + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.Coordinate} delta Delta. + * @return {ol.Coordinate} Coordinate. + */ +ol.coordinate.sub = function(coordinate, delta) { + coordinate[0] -= delta[0]; + coordinate[1] -= delta[1]; + return coordinate; +}; + + +/** + * @param {ol.Coordinate} coord1 First coordinate. + * @param {ol.Coordinate} coord2 Second coordinate. + * @return {number} Squared distance between coord1 and coord2. + */ +ol.coordinate.squaredDistance = function(coord1, coord2) { + var dx = coord1[0] - coord2[0]; + var dy = coord1[1] - coord2[1]; + return dx * dx + dy * dy; +}; + + +/** + * Calculate the squared distance from a coordinate to a line segment. + * + * @param {ol.Coordinate} coordinate Coordinate of the point. + * @param {Array.<ol.Coordinate>} segment Line segment (2 coordinates). + * @return {number} Squared distance from the point to the line segment. + */ +ol.coordinate.squaredDistanceToSegment = function(coordinate, segment) { + return ol.coordinate.squaredDistance(coordinate, + ol.coordinate.closestOnSegment(coordinate, segment)); +}; + + +/** + * Format a geographic coordinate with the hemisphere, degrees, minutes, and + * seconds. + * + * Example without specifying fractional digits: + * + * var coord = [7.85, 47.983333]; + * var out = ol.coordinate.toStringHDMS(coord); + * // out is now '47° 58′ 60″ N 7° 50′ 60″ E' + * + * Example explicitly specifying 1 fractional digit: + * + * var coord = [7.85, 47.983333]; + * var out = ol.coordinate.toStringHDMS(coord, 1); + * // out is now '47° 58′ 60.0″ N 7° 50′ 60.0″ E' + * + * @param {ol.Coordinate|undefined} coordinate Coordinate. + * @param {number=} opt_fractionDigits The number of digits to include + * after the decimal point. Default is `0`. + * @return {string} Hemisphere, degrees, minutes and seconds. + * @api stable + */ +ol.coordinate.toStringHDMS = function(coordinate, opt_fractionDigits) { + if (coordinate) { + return ol.coordinate.degreesToStringHDMS_(coordinate[1], 'NS', opt_fractionDigits) + ' ' + + ol.coordinate.degreesToStringHDMS_(coordinate[0], 'EW', opt_fractionDigits); + } else { + return ''; + } +}; + + +/** + * Format a coordinate as a comma delimited string. + * + * Example without specifying fractional digits: + * + * var coord = [7.85, 47.983333]; + * var out = ol.coordinate.toStringXY(coord); + * // out is now '8, 48' + * + * Example explicitly specifying 1 fractional digit: + * + * var coord = [7.85, 47.983333]; + * var out = ol.coordinate.toStringXY(coord, 1); + * // out is now '7.8, 48.0' + * + * @param {ol.Coordinate|undefined} coordinate Coordinate. + * @param {number=} opt_fractionDigits The number of digits to include + * after the decimal point. Default is `0`. + * @return {string} XY. + * @api stable + */ +ol.coordinate.toStringXY = function(coordinate, opt_fractionDigits) { + return ol.coordinate.format(coordinate, '{x}, {y}', opt_fractionDigits); +}; + + +/** + * Create an ol.Coordinate from an Array and take into account axis order. + * + * Examples: + * + * var northCoord = ol.coordinate.fromProjectedArray([1, 2], 'n'); + * // northCoord is now [2, 1] + * + * var eastCoord = ol.coordinate.fromProjectedArray([1, 2], 'e'); + * // eastCoord is now [1, 2] + * + * @param {Array} array The array with coordinates. + * @param {string} axis the axis info. + * @return {ol.Coordinate} The coordinate created. + */ +ol.coordinate.fromProjectedArray = function(array, axis) { + var firstAxis = axis.charAt(0); + if (firstAxis === 'n' || firstAxis === 's') { + return [array[1], array[0]]; + } else { + return array; + } +}; + +goog.provide('ol.extent'); +goog.provide('ol.extent.Corner'); +goog.provide('ol.extent.Relationship'); + +goog.require('goog.asserts'); + + +/** + * Extent corner. + * @enum {string} + */ +ol.extent.Corner = { + BOTTOM_LEFT: 'bottom-left', + BOTTOM_RIGHT: 'bottom-right', + TOP_LEFT: 'top-left', + TOP_RIGHT: 'top-right' +}; + + +/** + * Relationship to an extent. + * @enum {number} + */ +ol.extent.Relationship = { + UNKNOWN: 0, + INTERSECTING: 1, + ABOVE: 2, + RIGHT: 4, + BELOW: 8, + LEFT: 16 +}; + + +/** + * Build an extent that includes all given coordinates. + * + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @return {ol.Extent} Bounding extent. + * @api stable + */ +ol.extent.boundingExtent = function(coordinates) { + var extent = ol.extent.createEmpty(); + for (var i = 0, ii = coordinates.length; i < ii; ++i) { + ol.extent.extendCoordinate(extent, coordinates[i]); + } + return extent; +}; + + +/** + * @param {Array.<number>} xs Xs. + * @param {Array.<number>} ys Ys. + * @param {ol.Extent=} opt_extent Destination extent. + * @private + * @return {ol.Extent} Extent. + */ +ol.extent.boundingExtentXYs_ = function(xs, ys, opt_extent) { + goog.asserts.assert(xs.length > 0, 'xs length should be larger than 0'); + goog.asserts.assert(ys.length > 0, 'ys length should be larger than 0'); + var minX = Math.min.apply(null, xs); + var minY = Math.min.apply(null, ys); + var maxX = Math.max.apply(null, xs); + var maxY = Math.max.apply(null, ys); + return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent); +}; + + +/** + * Return extent increased by the provided value. + * @param {ol.Extent} extent Extent. + * @param {number} value The amount by which the extent should be buffered. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} Extent. + * @api stable + */ +ol.extent.buffer = function(extent, value, opt_extent) { + if (opt_extent) { + opt_extent[0] = extent[0] - value; + opt_extent[1] = extent[1] - value; + opt_extent[2] = extent[2] + value; + opt_extent[3] = extent[3] + value; + return opt_extent; + } else { + return [ + extent[0] - value, + extent[1] - value, + extent[2] + value, + extent[3] + value + ]; + } +}; + + +/** + * Creates a clone of an extent. + * + * @param {ol.Extent} extent Extent to clone. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} The clone. + */ +ol.extent.clone = function(extent, opt_extent) { + if (opt_extent) { + opt_extent[0] = extent[0]; + opt_extent[1] = extent[1]; + opt_extent[2] = extent[2]; + opt_extent[3] = extent[3]; + return opt_extent; + } else { + return extent.slice(); + } +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} x X. + * @param {number} y Y. + * @return {number} Closest squared distance. + */ +ol.extent.closestSquaredDistanceXY = function(extent, x, y) { + var dx, dy; + if (x < extent[0]) { + dx = extent[0] - x; + } else if (extent[2] < x) { + dx = x - extent[2]; + } else { + dx = 0; + } + if (y < extent[1]) { + dy = extent[1] - y; + } else if (extent[3] < y) { + dy = y - extent[3]; + } else { + dy = 0; + } + return dx * dx + dy * dy; +}; + + +/** + * Check if the passed coordinate is contained or on the edge of the extent. + * + * @param {ol.Extent} extent Extent. + * @param {ol.Coordinate} coordinate Coordinate. + * @return {boolean} The coordinate is contained in the extent. + * @api stable + */ +ol.extent.containsCoordinate = function(extent, coordinate) { + return ol.extent.containsXY(extent, coordinate[0], coordinate[1]); +}; + + +/** + * Check if one extent contains another. + * + * An extent is deemed contained if it lies completely within the other extent, + * including if they share one or more edges. + * + * @param {ol.Extent} extent1 Extent 1. + * @param {ol.Extent} extent2 Extent 2. + * @return {boolean} The second extent is contained by or on the edge of the + * first. + * @api stable + */ +ol.extent.containsExtent = function(extent1, extent2) { + return extent1[0] <= extent2[0] && extent2[2] <= extent1[2] && + extent1[1] <= extent2[1] && extent2[3] <= extent1[3]; +}; + + +/** + * Check if the passed coordinate is contained or on the edge of the extent. + * + * @param {ol.Extent} extent Extent. + * @param {number} x X coordinate. + * @param {number} y Y coordinate. + * @return {boolean} The x, y values are contained in the extent. + * @api stable + */ +ol.extent.containsXY = function(extent, x, y) { + return extent[0] <= x && x <= extent[2] && extent[1] <= y && y <= extent[3]; +}; + + +/** + * Get the relationship between a coordinate and extent. + * @param {ol.Extent} extent The extent. + * @param {ol.Coordinate} coordinate The coordinate. + * @return {number} The relationship (bitwise compare with + * ol.extent.Relationship). + */ +ol.extent.coordinateRelationship = function(extent, coordinate) { + var minX = extent[0]; + var minY = extent[1]; + var maxX = extent[2]; + var maxY = extent[3]; + var x = coordinate[0]; + var y = coordinate[1]; + var relationship = ol.extent.Relationship.UNKNOWN; + if (x < minX) { + relationship = relationship | ol.extent.Relationship.LEFT; + } else if (x > maxX) { + relationship = relationship | ol.extent.Relationship.RIGHT; + } + if (y < minY) { + relationship = relationship | ol.extent.Relationship.BELOW; + } else if (y > maxY) { + relationship = relationship | ol.extent.Relationship.ABOVE; + } + if (relationship === ol.extent.Relationship.UNKNOWN) { + relationship = ol.extent.Relationship.INTERSECTING; + } + return relationship; +}; + + +/** + * Create an empty extent. + * @return {ol.Extent} Empty extent. + * @api stable + */ +ol.extent.createEmpty = function() { + return [Infinity, Infinity, -Infinity, -Infinity]; +}; + + +/** + * Create a new extent or update the provided extent. + * @param {number} minX Minimum X. + * @param {number} minY Minimum Y. + * @param {number} maxX Maximum X. + * @param {number} maxY Maximum Y. + * @param {ol.Extent=} opt_extent Destination extent. + * @return {ol.Extent} Extent. + */ +ol.extent.createOrUpdate = function(minX, minY, maxX, maxY, opt_extent) { + if (opt_extent) { + opt_extent[0] = minX; + opt_extent[1] = minY; + opt_extent[2] = maxX; + opt_extent[3] = maxY; + return opt_extent; + } else { + return [minX, minY, maxX, maxY]; + } +}; + + +/** + * Create a new empty extent or make the provided one empty. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} Extent. + */ +ol.extent.createOrUpdateEmpty = function(opt_extent) { + return ol.extent.createOrUpdate( + Infinity, Infinity, -Infinity, -Infinity, opt_extent); +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} Extent. + */ +ol.extent.createOrUpdateFromCoordinate = function(coordinate, opt_extent) { + var x = coordinate[0]; + var y = coordinate[1]; + return ol.extent.createOrUpdate(x, y, x, y, opt_extent); +}; + + +/** + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} Extent. + */ +ol.extent.createOrUpdateFromCoordinates = function(coordinates, opt_extent) { + var extent = ol.extent.createOrUpdateEmpty(opt_extent); + return ol.extent.extendCoordinates(extent, coordinates); +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} Extent. + */ +ol.extent.createOrUpdateFromFlatCoordinates = function(flatCoordinates, offset, end, stride, opt_extent) { + var extent = ol.extent.createOrUpdateEmpty(opt_extent); + return ol.extent.extendFlatCoordinates( + extent, flatCoordinates, offset, end, stride); +}; + + +/** + * @param {Array.<Array.<ol.Coordinate>>} rings Rings. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} Extent. + */ +ol.extent.createOrUpdateFromRings = function(rings, opt_extent) { + var extent = ol.extent.createOrUpdateEmpty(opt_extent); + return ol.extent.extendRings(extent, rings); +}; + + +/** + * Empty an extent in place. + * @param {ol.Extent} extent Extent. + * @return {ol.Extent} Extent. + */ +ol.extent.empty = function(extent) { + extent[0] = extent[1] = Infinity; + extent[2] = extent[3] = -Infinity; + return extent; +}; + + +/** + * Determine if two extents are equivalent. + * @param {ol.Extent} extent1 Extent 1. + * @param {ol.Extent} extent2 Extent 2. + * @return {boolean} The two extents are equivalent. + * @api stable + */ +ol.extent.equals = function(extent1, extent2) { + return extent1[0] == extent2[0] && extent1[2] == extent2[2] && + extent1[1] == extent2[1] && extent1[3] == extent2[3]; +}; + + +/** + * Modify an extent to include another extent. + * @param {ol.Extent} extent1 The extent to be modified. + * @param {ol.Extent} extent2 The extent that will be included in the first. + * @return {ol.Extent} A reference to the first (extended) extent. + * @api stable + */ +ol.extent.extend = function(extent1, extent2) { + if (extent2[0] < extent1[0]) { + extent1[0] = extent2[0]; + } + if (extent2[2] > extent1[2]) { + extent1[2] = extent2[2]; + } + if (extent2[1] < extent1[1]) { + extent1[1] = extent2[1]; + } + if (extent2[3] > extent1[3]) { + extent1[3] = extent2[3]; + } + return extent1; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.Coordinate} coordinate Coordinate. + */ +ol.extent.extendCoordinate = function(extent, coordinate) { + if (coordinate[0] < extent[0]) { + extent[0] = coordinate[0]; + } + if (coordinate[0] > extent[2]) { + extent[2] = coordinate[0]; + } + if (coordinate[1] < extent[1]) { + extent[1] = coordinate[1]; + } + if (coordinate[1] > extent[3]) { + extent[3] = coordinate[1]; + } +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @return {ol.Extent} Extent. + */ +ol.extent.extendCoordinates = function(extent, coordinates) { + var i, ii; + for (i = 0, ii = coordinates.length; i < ii; ++i) { + ol.extent.extendCoordinate(extent, coordinates[i]); + } + return extent; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {ol.Extent} Extent. + */ +ol.extent.extendFlatCoordinates = function(extent, flatCoordinates, offset, end, stride) { + for (; offset < end; offset += stride) { + ol.extent.extendXY( + extent, flatCoordinates[offset], flatCoordinates[offset + 1]); + } + return extent; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {Array.<Array.<ol.Coordinate>>} rings Rings. + * @return {ol.Extent} Extent. + */ +ol.extent.extendRings = function(extent, rings) { + var i, ii; + for (i = 0, ii = rings.length; i < ii; ++i) { + ol.extent.extendCoordinates(extent, rings[i]); + } + return extent; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} x X. + * @param {number} y Y. + */ +ol.extent.extendXY = function(extent, x, y) { + extent[0] = Math.min(extent[0], x); + extent[1] = Math.min(extent[1], y); + extent[2] = Math.max(extent[2], x); + extent[3] = Math.max(extent[3], y); +}; + + +/** + * This function calls `callback` for each corner of the extent. If the + * callback returns a truthy value the function returns that value + * immediately. Otherwise the function returns `false`. + * @param {ol.Extent} extent Extent. + * @param {function(this:T, ol.Coordinate): S} callback Callback. + * @param {T=} opt_this Value to use as `this` when executing `callback`. + * @return {S|boolean} Value. + * @template S, T + */ +ol.extent.forEachCorner = function(extent, callback, opt_this) { + var val; + val = callback.call(opt_this, ol.extent.getBottomLeft(extent)); + if (val) { + return val; + } + val = callback.call(opt_this, ol.extent.getBottomRight(extent)); + if (val) { + return val; + } + val = callback.call(opt_this, ol.extent.getTopRight(extent)); + if (val) { + return val; + } + val = callback.call(opt_this, ol.extent.getTopLeft(extent)); + if (val) { + return val; + } + return false; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @return {number} Area. + */ +ol.extent.getArea = function(extent) { + var area = 0; + if (!ol.extent.isEmpty(extent)) { + area = ol.extent.getWidth(extent) * ol.extent.getHeight(extent); + } + return area; +}; + + +/** + * Get the bottom left coordinate of an extent. + * @param {ol.Extent} extent Extent. + * @return {ol.Coordinate} Bottom left coordinate. + * @api stable + */ +ol.extent.getBottomLeft = function(extent) { + return [extent[0], extent[1]]; +}; + + +/** + * Get the bottom right coordinate of an extent. + * @param {ol.Extent} extent Extent. + * @return {ol.Coordinate} Bottom right coordinate. + * @api stable + */ +ol.extent.getBottomRight = function(extent) { + return [extent[2], extent[1]]; +}; + + +/** + * Get the center coordinate of an extent. + * @param {ol.Extent} extent Extent. + * @return {ol.Coordinate} Center. + * @api stable + */ +ol.extent.getCenter = function(extent) { + return [(extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2]; +}; + + +/** + * Get a corner coordinate of an extent. + * @param {ol.Extent} extent Extent. + * @param {ol.extent.Corner} corner Corner. + * @return {ol.Coordinate} Corner coordinate. + */ +ol.extent.getCorner = function(extent, corner) { + var coordinate; + if (corner === ol.extent.Corner.BOTTOM_LEFT) { + coordinate = ol.extent.getBottomLeft(extent); + } else if (corner === ol.extent.Corner.BOTTOM_RIGHT) { + coordinate = ol.extent.getBottomRight(extent); + } else if (corner === ol.extent.Corner.TOP_LEFT) { + coordinate = ol.extent.getTopLeft(extent); + } else if (corner === ol.extent.Corner.TOP_RIGHT) { + coordinate = ol.extent.getTopRight(extent); + } else { + goog.asserts.fail('Invalid corner: %s', corner); + } + goog.asserts.assert(coordinate, 'coordinate should be defined'); + return coordinate; +}; + + +/** + * @param {ol.Extent} extent1 Extent 1. + * @param {ol.Extent} extent2 Extent 2. + * @return {number} Enlarged area. + */ +ol.extent.getEnlargedArea = function(extent1, extent2) { + var minX = Math.min(extent1[0], extent2[0]); + var minY = Math.min(extent1[1], extent2[1]); + var maxX = Math.max(extent1[2], extent2[2]); + var maxY = Math.max(extent1[3], extent2[3]); + return (maxX - minX) * (maxY - minY); +}; + + +/** + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {ol.Extent=} opt_extent Destination extent. + * @return {ol.Extent} Extent. + */ +ol.extent.getForViewAndSize = function(center, resolution, rotation, size, opt_extent) { + var dx = resolution * size[0] / 2; + var dy = resolution * size[1] / 2; + var cosRotation = Math.cos(rotation); + var sinRotation = Math.sin(rotation); + var xCos = dx * cosRotation; + var xSin = dx * sinRotation; + var yCos = dy * cosRotation; + var ySin = dy * sinRotation; + var x = center[0]; + var y = center[1]; + var x0 = x - xCos + ySin; + var x1 = x - xCos - ySin; + var x2 = x + xCos - ySin; + var x3 = x + xCos + ySin; + var y0 = y - xSin - yCos; + var y1 = y - xSin + yCos; + var y2 = y + xSin + yCos; + var y3 = y + xSin - yCos; + return ol.extent.createOrUpdate( + Math.min(x0, x1, x2, x3), Math.min(y0, y1, y2, y3), + Math.max(x0, x1, x2, x3), Math.max(y0, y1, y2, y3), + opt_extent); +}; + + +/** + * Get the height of an extent. + * @param {ol.Extent} extent Extent. + * @return {number} Height. + * @api stable + */ +ol.extent.getHeight = function(extent) { + return extent[3] - extent[1]; +}; + + +/** + * @param {ol.Extent} extent1 Extent 1. + * @param {ol.Extent} extent2 Extent 2. + * @return {number} Intersection area. + */ +ol.extent.getIntersectionArea = function(extent1, extent2) { + var intersection = ol.extent.getIntersection(extent1, extent2); + return ol.extent.getArea(intersection); +}; + + +/** + * Get the intersection of two extents. + * @param {ol.Extent} extent1 Extent 1. + * @param {ol.Extent} extent2 Extent 2. + * @param {ol.Extent=} opt_extent Optional extent to populate with intersection. + * @return {ol.Extent} Intersecting extent. + * @api stable + */ +ol.extent.getIntersection = function(extent1, extent2, opt_extent) { + var intersection = opt_extent ? opt_extent : ol.extent.createEmpty(); + if (ol.extent.intersects(extent1, extent2)) { + if (extent1[0] > extent2[0]) { + intersection[0] = extent1[0]; + } else { + intersection[0] = extent2[0]; + } + if (extent1[1] > extent2[1]) { + intersection[1] = extent1[1]; + } else { + intersection[1] = extent2[1]; + } + if (extent1[2] < extent2[2]) { + intersection[2] = extent1[2]; + } else { + intersection[2] = extent2[2]; + } + if (extent1[3] < extent2[3]) { + intersection[3] = extent1[3]; + } else { + intersection[3] = extent2[3]; + } + } + return intersection; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @return {number} Margin. + */ +ol.extent.getMargin = function(extent) { + return ol.extent.getWidth(extent) + ol.extent.getHeight(extent); +}; + + +/** + * Get the size (width, height) of an extent. + * @param {ol.Extent} extent The extent. + * @return {ol.Size} The extent size. + * @api stable + */ +ol.extent.getSize = function(extent) { + return [extent[2] - extent[0], extent[3] - extent[1]]; +}; + + +/** + * Get the top left coordinate of an extent. + * @param {ol.Extent} extent Extent. + * @return {ol.Coordinate} Top left coordinate. + * @api stable + */ +ol.extent.getTopLeft = function(extent) { + return [extent[0], extent[3]]; +}; + + +/** + * Get the top right coordinate of an extent. + * @param {ol.Extent} extent Extent. + * @return {ol.Coordinate} Top right coordinate. + * @api stable + */ +ol.extent.getTopRight = function(extent) { + return [extent[2], extent[3]]; +}; + + +/** + * Get the width of an extent. + * @param {ol.Extent} extent Extent. + * @return {number} Width. + * @api stable + */ +ol.extent.getWidth = function(extent) { + return extent[2] - extent[0]; +}; + + +/** + * Determine if one extent intersects another. + * @param {ol.Extent} extent1 Extent 1. + * @param {ol.Extent} extent2 Extent. + * @return {boolean} The two extents intersect. + * @api stable + */ +ol.extent.intersects = function(extent1, extent2) { + return extent1[0] <= extent2[2] && + extent1[2] >= extent2[0] && + extent1[1] <= extent2[3] && + extent1[3] >= extent2[1]; +}; + + +/** + * Determine if an extent is empty. + * @param {ol.Extent} extent Extent. + * @return {boolean} Is empty. + * @api stable + */ +ol.extent.isEmpty = function(extent) { + return extent[2] < extent[0] || extent[3] < extent[1]; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @return {boolean} Is infinite. + */ +ol.extent.isInfinite = function(extent) { + return extent[0] == -Infinity || extent[1] == -Infinity || + extent[2] == Infinity || extent[3] == Infinity; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.Coordinate} coordinate Coordinate. + * @return {ol.Coordinate} Coordinate. + */ +ol.extent.normalize = function(extent, coordinate) { + return [ + (coordinate[0] - extent[0]) / (extent[2] - extent[0]), + (coordinate[1] - extent[1]) / (extent[3] - extent[1]) + ]; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} Extent. + */ +ol.extent.returnOrUpdate = function(extent, opt_extent) { + if (opt_extent) { + opt_extent[0] = extent[0]; + opt_extent[1] = extent[1]; + opt_extent[2] = extent[2]; + opt_extent[3] = extent[3]; + return opt_extent; + } else { + return extent; + } +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} value Value. + */ +ol.extent.scaleFromCenter = function(extent, value) { + var deltaX = ((extent[2] - extent[0]) / 2) * (value - 1); + var deltaY = ((extent[3] - extent[1]) / 2) * (value - 1); + extent[0] -= deltaX; + extent[2] += deltaX; + extent[1] -= deltaY; + extent[3] += deltaY; +}; + + +/** + * Determine if the segment between two coordinates intersects (crosses, + * touches, or is contained by) the provided extent. + * @param {ol.Extent} extent The extent. + * @param {ol.Coordinate} start Segment start coordinate. + * @param {ol.Coordinate} end Segment end coordinate. + * @return {boolean} The segment intersects the extent. + */ +ol.extent.intersectsSegment = function(extent, start, end) { + var intersects = false; + var startRel = ol.extent.coordinateRelationship(extent, start); + var endRel = ol.extent.coordinateRelationship(extent, end); + if (startRel === ol.extent.Relationship.INTERSECTING || + endRel === ol.extent.Relationship.INTERSECTING) { + intersects = true; + } else { + var minX = extent[0]; + var minY = extent[1]; + var maxX = extent[2]; + var maxY = extent[3]; + var startX = start[0]; + var startY = start[1]; + var endX = end[0]; + var endY = end[1]; + var slope = (endY - startY) / (endX - startX); + var x, y; + if (!!(endRel & ol.extent.Relationship.ABOVE) && + !(startRel & ol.extent.Relationship.ABOVE)) { + // potentially intersects top + x = endX - ((endY - maxY) / slope); + intersects = x >= minX && x <= maxX; + } + if (!intersects && !!(endRel & ol.extent.Relationship.RIGHT) && + !(startRel & ol.extent.Relationship.RIGHT)) { + // potentially intersects right + y = endY - ((endX - maxX) * slope); + intersects = y >= minY && y <= maxY; + } + if (!intersects && !!(endRel & ol.extent.Relationship.BELOW) && + !(startRel & ol.extent.Relationship.BELOW)) { + // potentially intersects bottom + x = endX - ((endY - minY) / slope); + intersects = x >= minX && x <= maxX; + } + if (!intersects && !!(endRel & ol.extent.Relationship.LEFT) && + !(startRel & ol.extent.Relationship.LEFT)) { + // potentially intersects left + y = endY - ((endX - minX) * slope); + intersects = y >= minY && y <= maxY; + } + + } + return intersects; +}; + + +/** + * @param {ol.Extent} extent1 Extent 1. + * @param {ol.Extent} extent2 Extent 2. + * @return {boolean} Touches. + */ +ol.extent.touches = function(extent1, extent2) { + var intersects = ol.extent.intersects(extent1, extent2); + return intersects && + (extent1[0] == extent2[2] || extent1[2] == extent2[0] || + extent1[1] == extent2[3] || extent1[3] == extent2[1]); +}; + + +/** + * Apply a transform function to the extent. + * @param {ol.Extent} extent Extent. + * @param {ol.TransformFunction} transformFn Transform function. Called with + * [minX, minY, maxX, maxY] extent coordinates. + * @param {ol.Extent=} opt_extent Destination extent. + * @return {ol.Extent} Extent. + * @api stable + */ +ol.extent.applyTransform = function(extent, transformFn, opt_extent) { + var coordinates = [ + extent[0], extent[1], + extent[0], extent[3], + extent[2], extent[1], + extent[2], extent[3] + ]; + transformFn(coordinates, coordinates, 2); + var xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]]; + var ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]]; + return ol.extent.boundingExtentXYs_(xs, ys, opt_extent); +}; + +goog.provide('ol.functions'); + +/** + * Always returns true. + * @returns {boolean} true. + */ +ol.functions.TRUE = function() { + return true; +}; + +/** + * Always returns false. + * @returns {boolean} false. + */ +ol.functions.FALSE = function() { + return false; +}; + +/** + * @license + * Latitude/longitude spherical geodesy formulae taken from + * http://www.movable-type.co.uk/scripts/latlong.html + * Licensed under CC-BY-3.0. + */ + +goog.provide('ol.Sphere'); + +goog.require('ol.math'); + + +/** + * @classdesc + * Class to create objects that can be used with {@link + * ol.geom.Polygon.circular}. + * + * For example to create a sphere whose radius is equal to the semi-major + * axis of the WGS84 ellipsoid: + * + * ```js + * var wgs84Sphere= new ol.Sphere(6378137); + * ``` + * + * @constructor + * @param {number} radius Radius. + * @api + */ +ol.Sphere = function(radius) { + + /** + * @type {number} + */ + this.radius = radius; + +}; + + +/** + * Returns the geodesic area for a list of coordinates. + * + * [Reference](http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409) + * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for + * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion + * Laboratory, Pasadena, CA, June 2007 + * + * @param {Array.<ol.Coordinate>} coordinates List of coordinates of a linear + * ring. If the ring is oriented clockwise, the area will be positive, + * otherwise it will be negative. + * @return {number} Area. + * @api + */ +ol.Sphere.prototype.geodesicArea = function(coordinates) { + var area = 0, len = coordinates.length; + var x1 = coordinates[len - 1][0]; + var y1 = coordinates[len - 1][1]; + for (var i = 0; i < len; i++) { + var x2 = coordinates[i][0], y2 = coordinates[i][1]; + area += ol.math.toRadians(x2 - x1) * + (2 + Math.sin(ol.math.toRadians(y1)) + + Math.sin(ol.math.toRadians(y2))); + x1 = x2; + y1 = y2; + } + return area * this.radius * this.radius / 2.0; +}; + + +/** + * Returns the distance from c1 to c2 using the haversine formula. + * + * @param {ol.Coordinate} c1 Coordinate 1. + * @param {ol.Coordinate} c2 Coordinate 2. + * @return {number} Haversine distance. + * @api + */ +ol.Sphere.prototype.haversineDistance = function(c1, c2) { + var lat1 = ol.math.toRadians(c1[1]); + var lat2 = ol.math.toRadians(c2[1]); + var deltaLatBy2 = (lat2 - lat1) / 2; + var deltaLonBy2 = ol.math.toRadians(c2[0] - c1[0]) / 2; + var a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) + + Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) * + Math.cos(lat1) * Math.cos(lat2); + return 2 * this.radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); +}; + + +/** + * Returns the coordinate at the given distance and bearing from `c1`. + * + * @param {ol.Coordinate} c1 The origin point (`[lon, lat]` in degrees). + * @param {number} distance The great-circle distance between the origin + * point and the target point. + * @param {number} bearing The bearing (in radians). + * @return {ol.Coordinate} The target point. + */ +ol.Sphere.prototype.offset = function(c1, distance, bearing) { + var lat1 = ol.math.toRadians(c1[1]); + var lon1 = ol.math.toRadians(c1[0]); + var dByR = distance / this.radius; + var lat = Math.asin( + Math.sin(lat1) * Math.cos(dByR) + + Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)); + var lon = lon1 + Math.atan2( + Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1), + Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)); + return [ol.math.toDegrees(lon), ol.math.toDegrees(lat)]; +}; + +goog.provide('ol.sphere.NORMAL'); + +goog.require('ol.Sphere'); + + +/** + * The normal sphere. + * @const + * @type {ol.Sphere} + */ +ol.sphere.NORMAL = new ol.Sphere(6370997); + +goog.provide('ol.proj'); +goog.provide('ol.proj.METERS_PER_UNIT'); +goog.provide('ol.proj.Projection'); +goog.provide('ol.proj.Units'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.sphere.NORMAL'); + + +/** + * Projection units: `'degrees'`, `'ft'`, `'m'`, `'pixels'`, `'tile-pixels'` or + * `'us-ft'`. + * @enum {string} + */ +ol.proj.Units = { + DEGREES: 'degrees', + FEET: 'ft', + METERS: 'm', + PIXELS: 'pixels', + TILE_PIXELS: 'tile-pixels', + USFEET: 'us-ft' +}; + + +/** + * Meters per unit lookup table. + * @const + * @type {Object.<ol.proj.Units, number>} + * @api stable + */ +ol.proj.METERS_PER_UNIT = {}; +ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] = + 2 * Math.PI * ol.sphere.NORMAL.radius / 360; +ol.proj.METERS_PER_UNIT[ol.proj.Units.FEET] = 0.3048; +ol.proj.METERS_PER_UNIT[ol.proj.Units.METERS] = 1; +ol.proj.METERS_PER_UNIT[ol.proj.Units.USFEET] = 1200 / 3937; + + +/** + * @classdesc + * Projection definition class. One of these is created for each projection + * supported in the application and stored in the {@link ol.proj} namespace. + * You can use these in applications, but this is not required, as API params + * and options use {@link ol.ProjectionLike} which means the simple string + * code will suffice. + * + * You can use {@link ol.proj.get} to retrieve the object for a particular + * projection. + * + * The library includes definitions for `EPSG:4326` and `EPSG:3857`, together + * with the following aliases: + * * `EPSG:4326`: CRS:84, urn:ogc:def:crs:EPSG:6.6:4326, + * urn:ogc:def:crs:OGC:1.3:CRS84, urn:ogc:def:crs:OGC:2:84, + * http://www.opengis.net/gml/srs/epsg.xml#4326, + * urn:x-ogc:def:crs:EPSG:4326 + * * `EPSG:3857`: EPSG:102100, EPSG:102113, EPSG:900913, + * urn:ogc:def:crs:EPSG:6.18:3:3857, + * http://www.opengis.net/gml/srs/epsg.xml#3857 + * + * If you use proj4js, aliases can be added using `proj4.defs()`; see + * [documentation](https://github.com/proj4js/proj4js). To set an alternative + * namespace for proj4, use {@link ol.proj.setProj4}. + * + * @constructor + * @param {olx.ProjectionOptions} options Projection options. + * @struct + * @api stable + */ +ol.proj.Projection = function(options) { + + /** + * @private + * @type {string} + */ + this.code_ = options.code; + + /** + * @private + * @type {ol.proj.Units} + */ + this.units_ = /** @type {ol.proj.Units} */ (options.units); + + /** + * @private + * @type {ol.Extent} + */ + this.extent_ = options.extent !== undefined ? options.extent : null; + + /** + * @private + * @type {ol.Extent} + */ + this.worldExtent_ = options.worldExtent !== undefined ? + options.worldExtent : null; + + /** + * @private + * @type {string} + */ + this.axisOrientation_ = options.axisOrientation !== undefined ? + options.axisOrientation : 'enu'; + + /** + * @private + * @type {boolean} + */ + this.global_ = options.global !== undefined ? options.global : false; + + + /** + * @private + * @type {boolean} + */ + this.canWrapX_ = !!(this.global_ && this.extent_); + + /** + * @private + * @type {function(number, ol.Coordinate):number} + */ + this.getPointResolutionFunc_ = options.getPointResolution !== undefined ? + options.getPointResolution : this.getPointResolution_; + + /** + * @private + * @type {ol.tilegrid.TileGrid} + */ + this.defaultTileGrid_ = null; + + /** + * @private + * @type {number|undefined} + */ + this.metersPerUnit_ = options.metersPerUnit; + + var projections = ol.proj.projections_; + var code = options.code; + goog.asserts.assert(code !== undefined, + 'Option "code" is required for constructing instance'); + if (ol.ENABLE_PROJ4JS) { + var proj4js = ol.proj.proj4_ || ol.global['proj4']; + if (typeof proj4js == 'function' && projections[code] === undefined) { + var def = proj4js.defs(code); + if (def !== undefined) { + if (def.axis !== undefined && options.axisOrientation === undefined) { + this.axisOrientation_ = def.axis; + } + if (options.metersPerUnit === undefined) { + this.metersPerUnit_ = def.to_meter; + } + if (options.units === undefined) { + this.units_ = def.units; + } + var currentCode, currentDef, currentProj, proj4Transform; + for (currentCode in projections) { + currentDef = proj4js.defs(currentCode); + if (currentDef !== undefined) { + currentProj = ol.proj.get(currentCode); + if (currentDef === def) { + ol.proj.addEquivalentProjections([currentProj, this]); + } else { + proj4Transform = proj4js(currentCode, code); + ol.proj.addCoordinateTransforms(currentProj, this, + proj4Transform.forward, proj4Transform.inverse); + } + } + } + } + } + } + +}; + + +/** + * @return {boolean} The projection is suitable for wrapping the x-axis + */ +ol.proj.Projection.prototype.canWrapX = function() { + return this.canWrapX_; +}; + + +/** + * Get the code for this projection, e.g. 'EPSG:4326'. + * @return {string} Code. + * @api stable + */ +ol.proj.Projection.prototype.getCode = function() { + return this.code_; +}; + + +/** + * Get the validity extent for this projection. + * @return {ol.Extent} Extent. + * @api stable + */ +ol.proj.Projection.prototype.getExtent = function() { + return this.extent_; +}; + + +/** + * Get the units of this projection. + * @return {ol.proj.Units} Units. + * @api stable + */ +ol.proj.Projection.prototype.getUnits = function() { + return this.units_; +}; + + +/** + * Get the amount of meters per unit of this projection. If the projection is + * not configured with `metersPerUnit` or a units identifier, the return is + * `undefined`. + * @return {number|undefined} Meters. + * @api stable + */ +ol.proj.Projection.prototype.getMetersPerUnit = function() { + return this.metersPerUnit_ || ol.proj.METERS_PER_UNIT[this.units_]; +}; + + +/** + * Get the world extent for this projection. + * @return {ol.Extent} Extent. + * @api + */ +ol.proj.Projection.prototype.getWorldExtent = function() { + return this.worldExtent_; +}; + + +/** + * Get the axis orientation of this projection. + * Example values are: + * enu - the default easting, northing, elevation. + * neu - northing, easting, up - useful for "lat/long" geographic coordinates, + * or south orientated transverse mercator. + * wnu - westing, northing, up - some planetary coordinate systems have + * "west positive" coordinate systems + * @return {string} Axis orientation. + */ +ol.proj.Projection.prototype.getAxisOrientation = function() { + return this.axisOrientation_; +}; + + +/** + * Is this projection a global projection which spans the whole world? + * @return {boolean} Whether the projection is global. + * @api stable + */ +ol.proj.Projection.prototype.isGlobal = function() { + return this.global_; +}; + + +/** +* Set if the projection is a global projection which spans the whole world +* @param {boolean} global Whether the projection is global. +* @api stable +*/ +ol.proj.Projection.prototype.setGlobal = function(global) { + this.global_ = global; + this.canWrapX_ = !!(global && this.extent_); +}; + + +/** + * @return {ol.tilegrid.TileGrid} The default tile grid. + */ +ol.proj.Projection.prototype.getDefaultTileGrid = function() { + return this.defaultTileGrid_; +}; + + +/** + * @param {ol.tilegrid.TileGrid} tileGrid The default tile grid. + */ +ol.proj.Projection.prototype.setDefaultTileGrid = function(tileGrid) { + this.defaultTileGrid_ = tileGrid; +}; + + +/** + * Set the validity extent for this projection. + * @param {ol.Extent} extent Extent. + * @api stable + */ +ol.proj.Projection.prototype.setExtent = function(extent) { + this.extent_ = extent; + this.canWrapX_ = !!(this.global_ && extent); +}; + + +/** + * Set the world extent for this projection. + * @param {ol.Extent} worldExtent World extent + * [minlon, minlat, maxlon, maxlat]. + * @api + */ +ol.proj.Projection.prototype.setWorldExtent = function(worldExtent) { + this.worldExtent_ = worldExtent; +}; + + +/** +* Set the getPointResolution function for this projection. +* @param {function(number, ol.Coordinate):number} func Function +* @api +*/ +ol.proj.Projection.prototype.setGetPointResolution = function(func) { + this.getPointResolutionFunc_ = func; +}; + + +/** +* Default version. +* Get the resolution of the point in degrees or distance units. +* For projections with degrees as the unit this will simply return the +* provided resolution. For other projections the point resolution is +* estimated by transforming the 'point' pixel to EPSG:4326, +* measuring its width and height on the normal sphere, +* and taking the average of the width and height. +* @param {number} resolution Nominal resolution in projection units. +* @param {ol.Coordinate} point Point to find adjusted resolution at. +* @return {number} Point resolution at point in projection units. +* @private +*/ +ol.proj.Projection.prototype.getPointResolution_ = function(resolution, point) { + var units = this.getUnits(); + if (units == ol.proj.Units.DEGREES) { + return resolution; + } else { + // Estimate point resolution by transforming the center pixel to EPSG:4326, + // measuring its width and height on the normal sphere, and taking the + // average of the width and height. + var toEPSG4326 = ol.proj.getTransformFromProjections( + this, ol.proj.get('EPSG:4326')); + var vertices = [ + point[0] - resolution / 2, point[1], + point[0] + resolution / 2, point[1], + point[0], point[1] - resolution / 2, + point[0], point[1] + resolution / 2 + ]; + vertices = toEPSG4326(vertices, vertices, 2); + var width = ol.sphere.NORMAL.haversineDistance( + vertices.slice(0, 2), vertices.slice(2, 4)); + var height = ol.sphere.NORMAL.haversineDistance( + vertices.slice(4, 6), vertices.slice(6, 8)); + var pointResolution = (width + height) / 2; + var metersPerUnit = this.getMetersPerUnit(); + if (metersPerUnit !== undefined) { + pointResolution /= metersPerUnit; + } + return pointResolution; + } +}; + + +/** + * Get the resolution of the point in degrees or distance units. + * For projections with degrees as the unit this will simply return the + * provided resolution. The default for other projections is to estimate + * the point resolution by transforming the 'point' pixel to EPSG:4326, + * measuring its width and height on the normal sphere, + * and taking the average of the width and height. + * An alternative implementation may be given when constructing a + * projection. For many local projections, + * such a custom function will return the resolution unchanged. + * @param {number} resolution Resolution in projection units. + * @param {ol.Coordinate} point Point. + * @return {number} Point resolution in projection units. + * @api + */ +ol.proj.Projection.prototype.getPointResolution = function(resolution, point) { + return this.getPointResolutionFunc_(resolution, point); +}; + + +/** + * @private + * @type {Object.<string, ol.proj.Projection>} + */ +ol.proj.projections_ = {}; + + +/** + * @private + * @type {Object.<string, Object.<string, ol.TransformFunction>>} + */ +ol.proj.transforms_ = {}; + + +/** + * @private + * @type {proj4} + */ +ol.proj.proj4_ = null; + + +if (ol.ENABLE_PROJ4JS) { + /** + * Register proj4. If not explicitly registered, it will be assumed that + * proj4js will be loaded in the global namespace. For example in a + * browserify ES6 environment you could use: + * + * import ol from 'openlayers'; + * import proj4 from 'proj4'; + * ol.proj.setProj4(proj4); + * + * @param {proj4} proj4 Proj4. + * @api + */ + ol.proj.setProj4 = function(proj4) { + goog.asserts.assert(typeof proj4 == 'function', + 'proj4 argument should be a function'); + ol.proj.proj4_ = proj4; + }; +} + + +/** + * Registers transformation functions that don't alter coordinates. Those allow + * to transform between projections with equal meaning. + * + * @param {Array.<ol.proj.Projection>} projections Projections. + * @api + */ +ol.proj.addEquivalentProjections = function(projections) { + ol.proj.addProjections(projections); + projections.forEach(function(source) { + projections.forEach(function(destination) { + if (source !== destination) { + ol.proj.addTransform(source, destination, ol.proj.cloneTransform); + } + }); + }); +}; + + +/** + * Registers transformation functions to convert coordinates in any projection + * in projection1 to any projection in projection2. + * + * @param {Array.<ol.proj.Projection>} projections1 Projections with equal + * meaning. + * @param {Array.<ol.proj.Projection>} projections2 Projections with equal + * meaning. + * @param {ol.TransformFunction} forwardTransform Transformation from any + * projection in projection1 to any projection in projection2. + * @param {ol.TransformFunction} inverseTransform Transform from any projection + * in projection2 to any projection in projection1.. + */ +ol.proj.addEquivalentTransforms = function(projections1, projections2, forwardTransform, inverseTransform) { + projections1.forEach(function(projection1) { + projections2.forEach(function(projection2) { + ol.proj.addTransform(projection1, projection2, forwardTransform); + ol.proj.addTransform(projection2, projection1, inverseTransform); + }); + }); +}; + + +/** + * Add a Projection object to the list of supported projections that can be + * looked up by their code. + * + * @param {ol.proj.Projection} projection Projection instance. + * @api stable + */ +ol.proj.addProjection = function(projection) { + ol.proj.projections_[projection.getCode()] = projection; + ol.proj.addTransform(projection, projection, ol.proj.cloneTransform); +}; + + +/** + * @param {Array.<ol.proj.Projection>} projections Projections. + */ +ol.proj.addProjections = function(projections) { + var addedProjections = []; + projections.forEach(function(projection) { + addedProjections.push(ol.proj.addProjection(projection)); + }); +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.proj.clearAllProjections = function() { + ol.proj.projections_ = {}; + ol.proj.transforms_ = {}; +}; + + +/** + * @param {ol.proj.Projection|string|undefined} projection Projection. + * @param {string} defaultCode Default code. + * @return {ol.proj.Projection} Projection. + */ +ol.proj.createProjection = function(projection, defaultCode) { + if (!projection) { + return ol.proj.get(defaultCode); + } else if (typeof projection === 'string') { + return ol.proj.get(projection); + } else { + goog.asserts.assertInstanceof(projection, ol.proj.Projection, + 'projection should be an ol.proj.Projection'); + return projection; + } +}; + + +/** + * Registers a conversion function to convert coordinates from the source + * projection to the destination projection. + * + * @param {ol.proj.Projection} source Source. + * @param {ol.proj.Projection} destination Destination. + * @param {ol.TransformFunction} transformFn Transform. + */ +ol.proj.addTransform = function(source, destination, transformFn) { + var sourceCode = source.getCode(); + var destinationCode = destination.getCode(); + var transforms = ol.proj.transforms_; + if (!(sourceCode in transforms)) { + transforms[sourceCode] = {}; + } + transforms[sourceCode][destinationCode] = transformFn; +}; + + +/** + * Registers coordinate transform functions to convert coordinates between the + * source projection and the destination projection. + * The forward and inverse functions convert coordinate pairs; this function + * converts these into the functions used internally which also handle + * extents and coordinate arrays. + * + * @param {ol.ProjectionLike} source Source projection. + * @param {ol.ProjectionLike} destination Destination projection. + * @param {function(ol.Coordinate): ol.Coordinate} forward The forward transform + * function (that is, from the source projection to the destination + * projection) that takes a {@link ol.Coordinate} as argument and returns + * the transformed {@link ol.Coordinate}. + * @param {function(ol.Coordinate): ol.Coordinate} inverse The inverse transform + * function (that is, from the destination projection to the source + * projection) that takes a {@link ol.Coordinate} as argument and returns + * the transformed {@link ol.Coordinate}. + * @api stable + */ +ol.proj.addCoordinateTransforms = function(source, destination, forward, inverse) { + var sourceProj = ol.proj.get(source); + var destProj = ol.proj.get(destination); + ol.proj.addTransform(sourceProj, destProj, + ol.proj.createTransformFromCoordinateTransform(forward)); + ol.proj.addTransform(destProj, sourceProj, + ol.proj.createTransformFromCoordinateTransform(inverse)); +}; + + +/** + * Creates a {@link ol.TransformFunction} from a simple 2D coordinate transform + * function. + * @param {function(ol.Coordinate): ol.Coordinate} transform Coordinate + * transform. + * @return {ol.TransformFunction} Transform function. + */ +ol.proj.createTransformFromCoordinateTransform = function(transform) { + return ( + /** + * @param {Array.<number>} input Input. + * @param {Array.<number>=} opt_output Output. + * @param {number=} opt_dimension Dimension. + * @return {Array.<number>} Output. + */ + function(input, opt_output, opt_dimension) { + var length = input.length; + var dimension = opt_dimension !== undefined ? opt_dimension : 2; + var output = opt_output !== undefined ? opt_output : new Array(length); + var point, i, j; + for (i = 0; i < length; i += dimension) { + point = transform([input[i], input[i + 1]]); + output[i] = point[0]; + output[i + 1] = point[1]; + for (j = dimension - 1; j >= 2; --j) { + output[i + j] = input[i + j]; + } + } + return output; + }); +}; + + +/** + * Unregisters the conversion function to convert coordinates from the source + * projection to the destination projection. This method is used to clean up + * cached transforms during testing. + * + * @param {ol.proj.Projection} source Source projection. + * @param {ol.proj.Projection} destination Destination projection. + * @return {ol.TransformFunction} transformFn The unregistered transform. + */ +ol.proj.removeTransform = function(source, destination) { + var sourceCode = source.getCode(); + var destinationCode = destination.getCode(); + var transforms = ol.proj.transforms_; + goog.asserts.assert(sourceCode in transforms, + 'sourceCode should be in transforms'); + goog.asserts.assert(destinationCode in transforms[sourceCode], + 'destinationCode should be in transforms of sourceCode'); + var transform = transforms[sourceCode][destinationCode]; + delete transforms[sourceCode][destinationCode]; + if (ol.object.isEmpty(transforms[sourceCode])) { + delete transforms[sourceCode]; + } + return transform; +}; + + +/** + * Transforms a coordinate from longitude/latitude to a different projection. + * @param {ol.Coordinate} coordinate Coordinate as longitude and latitude, i.e. + * an array with longitude as 1st and latitude as 2nd element. + * @param {ol.ProjectionLike=} opt_projection Target projection. The + * default is Web Mercator, i.e. 'EPSG:3857'. + * @return {ol.Coordinate} Coordinate projected to the target projection. + * @api stable + */ +ol.proj.fromLonLat = function(coordinate, opt_projection) { + return ol.proj.transform(coordinate, 'EPSG:4326', + opt_projection !== undefined ? opt_projection : 'EPSG:3857'); +}; + + +/** + * Transforms a coordinate to longitude/latitude. + * @param {ol.Coordinate} coordinate Projected coordinate. + * @param {ol.ProjectionLike=} opt_projection Projection of the coordinate. + * The default is Web Mercator, i.e. 'EPSG:3857'. + * @return {ol.Coordinate} Coordinate as longitude and latitude, i.e. an array + * with longitude as 1st and latitude as 2nd element. + * @api stable + */ +ol.proj.toLonLat = function(coordinate, opt_projection) { + return ol.proj.transform(coordinate, + opt_projection !== undefined ? opt_projection : 'EPSG:3857', 'EPSG:4326'); +}; + + +/** + * Fetches a Projection object for the code specified. + * + * @param {ol.ProjectionLike} projectionLike Either a code string which is + * a combination of authority and identifier such as "EPSG:4326", or an + * existing projection object, or undefined. + * @return {ol.proj.Projection} Projection object, or null if not in list. + * @api stable + */ +ol.proj.get = function(projectionLike) { + var projection; + if (projectionLike instanceof ol.proj.Projection) { + projection = projectionLike; + } else if (typeof projectionLike === 'string') { + var code = projectionLike; + projection = ol.proj.projections_[code]; + if (ol.ENABLE_PROJ4JS) { + var proj4js = ol.proj.proj4_ || ol.global['proj4']; + if (projection === undefined && typeof proj4js == 'function' && + proj4js.defs(code) !== undefined) { + projection = new ol.proj.Projection({code: code}); + ol.proj.addProjection(projection); + } + } + } else { + projection = null; + } + return projection; +}; + + +/** + * Checks if two projections are the same, that is every coordinate in one + * projection does represent the same geographic point as the same coordinate in + * the other projection. + * + * @param {ol.proj.Projection} projection1 Projection 1. + * @param {ol.proj.Projection} projection2 Projection 2. + * @return {boolean} Equivalent. + * @api + */ +ol.proj.equivalent = function(projection1, projection2) { + if (projection1 === projection2) { + return true; + } + var equalUnits = projection1.getUnits() === projection2.getUnits(); + if (projection1.getCode() === projection2.getCode()) { + return equalUnits; + } else { + var transformFn = ol.proj.getTransformFromProjections( + projection1, projection2); + return transformFn === ol.proj.cloneTransform && equalUnits; + } +}; + + +/** + * Given the projection-like objects, searches for a transformation + * function to convert a coordinates array from the source projection to the + * destination projection. + * + * @param {ol.ProjectionLike} source Source. + * @param {ol.ProjectionLike} destination Destination. + * @return {ol.TransformFunction} Transform function. + * @api stable + */ +ol.proj.getTransform = function(source, destination) { + var sourceProjection = ol.proj.get(source); + var destinationProjection = ol.proj.get(destination); + return ol.proj.getTransformFromProjections( + sourceProjection, destinationProjection); +}; + + +/** + * Searches in the list of transform functions for the function for converting + * coordinates from the source projection to the destination projection. + * + * @param {ol.proj.Projection} sourceProjection Source Projection object. + * @param {ol.proj.Projection} destinationProjection Destination Projection + * object. + * @return {ol.TransformFunction} Transform function. + */ +ol.proj.getTransformFromProjections = function(sourceProjection, destinationProjection) { + var transforms = ol.proj.transforms_; + var sourceCode = sourceProjection.getCode(); + var destinationCode = destinationProjection.getCode(); + var transform; + if (sourceCode in transforms && destinationCode in transforms[sourceCode]) { + transform = transforms[sourceCode][destinationCode]; + } + if (transform === undefined) { + goog.asserts.assert(transform !== undefined, 'transform should be defined'); + transform = ol.proj.identityTransform; + } + return transform; +}; + + +/** + * @param {Array.<number>} input Input coordinate array. + * @param {Array.<number>=} opt_output Output array of coordinate values. + * @param {number=} opt_dimension Dimension. + * @return {Array.<number>} Input coordinate array (same array as input). + */ +ol.proj.identityTransform = function(input, opt_output, opt_dimension) { + if (opt_output !== undefined && input !== opt_output) { + // TODO: consider making this a warning instead + goog.asserts.fail('This should not be used internally.'); + for (var i = 0, ii = input.length; i < ii; ++i) { + opt_output[i] = input[i]; + } + input = opt_output; + } + return input; +}; + + +/** + * @param {Array.<number>} input Input coordinate array. + * @param {Array.<number>=} opt_output Output array of coordinate values. + * @param {number=} opt_dimension Dimension. + * @return {Array.<number>} Output coordinate array (new array, same coordinate + * values). + */ +ol.proj.cloneTransform = function(input, opt_output, opt_dimension) { + var output; + if (opt_output !== undefined) { + for (var i = 0, ii = input.length; i < ii; ++i) { + opt_output[i] = input[i]; + } + output = opt_output; + } else { + output = input.slice(); + } + return output; +}; + + +/** + * Transforms a coordinate from source projection to destination projection. + * This returns a new coordinate (and does not modify the original). + * + * See {@link ol.proj.transformExtent} for extent transformation. + * See the transform method of {@link ol.geom.Geometry} and its subclasses for + * geometry transforms. + * + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.ProjectionLike} source Source projection-like. + * @param {ol.ProjectionLike} destination Destination projection-like. + * @return {ol.Coordinate} Coordinate. + * @api stable + */ +ol.proj.transform = function(coordinate, source, destination) { + var transformFn = ol.proj.getTransform(source, destination); + return transformFn(coordinate, undefined, coordinate.length); +}; + + +/** + * Transforms an extent from source projection to destination projection. This + * returns a new extent (and does not modify the original). + * + * @param {ol.Extent} extent The extent to transform. + * @param {ol.ProjectionLike} source Source projection-like. + * @param {ol.ProjectionLike} destination Destination projection-like. + * @return {ol.Extent} The transformed extent. + * @api stable + */ +ol.proj.transformExtent = function(extent, source, destination) { + var transformFn = ol.proj.getTransform(source, destination); + return ol.extent.applyTransform(extent, transformFn); +}; + + +/** + * Transforms the given point to the destination projection. + * + * @param {ol.Coordinate} point Point. + * @param {ol.proj.Projection} sourceProjection Source projection. + * @param {ol.proj.Projection} destinationProjection Destination projection. + * @return {ol.Coordinate} Point. + */ +ol.proj.transformWithProjections = function(point, sourceProjection, destinationProjection) { + var transformFn = ol.proj.getTransformFromProjections( + sourceProjection, destinationProjection); + return transformFn(point); +}; + +goog.provide('ol.geom.Geometry'); +goog.provide('ol.geom.GeometryLayout'); +goog.provide('ol.geom.GeometryType'); + +goog.require('goog.asserts'); +goog.require('ol.functions'); +goog.require('ol.Object'); +goog.require('ol.extent'); +goog.require('ol.proj'); +goog.require('ol.proj.Units'); + + +/** + * The geometry type. One of `'Point'`, `'LineString'`, `'LinearRing'`, + * `'Polygon'`, `'MultiPoint'`, `'MultiLineString'`, `'MultiPolygon'`, + * `'GeometryCollection'`, `'Circle'`. + * @enum {string} + */ +ol.geom.GeometryType = { + POINT: 'Point', + LINE_STRING: 'LineString', + LINEAR_RING: 'LinearRing', + POLYGON: 'Polygon', + MULTI_POINT: 'MultiPoint', + MULTI_LINE_STRING: 'MultiLineString', + MULTI_POLYGON: 'MultiPolygon', + GEOMETRY_COLLECTION: 'GeometryCollection', + CIRCLE: 'Circle' +}; + + +/** + * The coordinate layout for geometries, indicating whether a 3rd or 4th z ('Z') + * or measure ('M') coordinate is available. Supported values are `'XY'`, + * `'XYZ'`, `'XYM'`, `'XYZM'`. + * @enum {string} + */ +ol.geom.GeometryLayout = { + XY: 'XY', + XYZ: 'XYZ', + XYM: 'XYM', + XYZM: 'XYZM' +}; + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for vector geometries. + * + * To get notified of changes to the geometry, register a listener for the + * generic `change` event on your geometry instance. + * + * @constructor + * @extends {ol.Object} + * @api stable + */ +ol.geom.Geometry = function() { + + ol.Object.call(this); + + /** + * @private + * @type {ol.Extent} + */ + this.extent_ = ol.extent.createEmpty(); + + /** + * @private + * @type {number} + */ + this.extentRevision_ = -1; + + /** + * @protected + * @type {Object.<string, ol.geom.Geometry>} + */ + this.simplifiedGeometryCache = {}; + + /** + * @protected + * @type {number} + */ + this.simplifiedGeometryMaxMinSquaredTolerance = 0; + + /** + * @protected + * @type {number} + */ + this.simplifiedGeometryRevision = 0; + +}; +ol.inherits(ol.geom.Geometry, ol.Object); + + +/** + * Make a complete copy of the geometry. + * @function + * @return {!ol.geom.Geometry} Clone. + */ +ol.geom.Geometry.prototype.clone = goog.abstractMethod; + + +/** + * @param {number} x X. + * @param {number} y Y. + * @param {ol.Coordinate} closestPoint Closest point. + * @param {number} minSquaredDistance Minimum squared distance. + * @return {number} Minimum squared distance. + */ +ol.geom.Geometry.prototype.closestPointXY = goog.abstractMethod; + + +/** + * Return the closest point of the geometry to the passed point as + * {@link ol.Coordinate coordinate}. + * @param {ol.Coordinate} point Point. + * @param {ol.Coordinate=} opt_closestPoint Closest point. + * @return {ol.Coordinate} Closest point. + * @api stable + */ +ol.geom.Geometry.prototype.getClosestPoint = function(point, opt_closestPoint) { + var closestPoint = opt_closestPoint ? opt_closestPoint : [NaN, NaN]; + this.closestPointXY(point[0], point[1], closestPoint, Infinity); + return closestPoint; +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @return {boolean} Contains coordinate. + */ +ol.geom.Geometry.prototype.containsCoordinate = function(coordinate) { + return this.containsXY(coordinate[0], coordinate[1]); +}; + + +/** + * @param {ol.Extent} extent Extent. + * @protected + * @return {ol.Extent} extent Extent. + */ +ol.geom.Geometry.prototype.computeExtent = goog.abstractMethod; + + +/** + * @param {number} x X. + * @param {number} y Y. + * @return {boolean} Contains (x, y). + */ +ol.geom.Geometry.prototype.containsXY = ol.functions.FALSE; + + +/** + * Get the extent of the geometry. + * @param {ol.Extent=} opt_extent Extent. + * @return {ol.Extent} extent Extent. + * @api stable + */ +ol.geom.Geometry.prototype.getExtent = function(opt_extent) { + if (this.extentRevision_ != this.getRevision()) { + this.extent_ = this.computeExtent(this.extent_); + this.extentRevision_ = this.getRevision(); + } + return ol.extent.returnOrUpdate(this.extent_, opt_extent); +}; + + +/** + * Rotate the geometry around a given coordinate. This modifies the geometry + * coordinates in place. + * @param {number} angle Rotation angle in radians. + * @param {ol.Coordinate} anchor The rotation center. + * @api + * @function + */ +ol.geom.Geometry.prototype.rotate = goog.abstractMethod; + + +/** + * Create a simplified version of this geometry. For linestrings, this uses + * the the {@link + * https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + * Douglas Peucker} algorithm. For polygons, a quantization-based + * simplification is used to preserve topology. + * @function + * @param {number} tolerance The tolerance distance for simplification. + * @return {ol.geom.Geometry} A new, simplified version of the original + * geometry. + * @api + */ +ol.geom.Geometry.prototype.simplify = function(tolerance) { + return this.getSimplifiedGeometry(tolerance * tolerance); +}; + + +/** + * Create a simplified version of this geometry using the Douglas Peucker + * algorithm. + * @see https://en.wikipedia.org/wiki/Ramer-Douglas-Peucker_algorithm + * @function + * @param {number} squaredTolerance Squared tolerance. + * @return {ol.geom.Geometry} Simplified geometry. + */ +ol.geom.Geometry.prototype.getSimplifiedGeometry = goog.abstractMethod; + + +/** + * Get the type of this geometry. + * @function + * @return {ol.geom.GeometryType} Geometry type. + */ +ol.geom.Geometry.prototype.getType = goog.abstractMethod; + + +/** + * Apply a transform function to each coordinate of the geometry. + * The geometry is modified in place. + * If you do not want the geometry modified in place, first `clone()` it and + * then use this function on the clone. + * @function + * @param {ol.TransformFunction} transformFn Transform. + */ +ol.geom.Geometry.prototype.applyTransform = goog.abstractMethod; + + +/** + * Test if the geometry and the passed extent intersect. + * @param {ol.Extent} extent Extent. + * @return {boolean} `true` if the geometry and the extent intersect. + * @function + */ +ol.geom.Geometry.prototype.intersectsExtent = goog.abstractMethod; + + +/** + * Translate the geometry. This modifies the geometry coordinates in place. If + * instead you want a new geometry, first `clone()` this geometry. + * @param {number} deltaX Delta X. + * @param {number} deltaY Delta Y. + * @function + */ +ol.geom.Geometry.prototype.translate = goog.abstractMethod; + + +/** + * Transform each coordinate of the geometry from one coordinate reference + * system to another. The geometry is modified in place. + * For example, a line will be transformed to a line and a circle to a circle. + * If you do not want the geometry modified in place, first `clone()` it and + * then use this function on the clone. + * + * @param {ol.ProjectionLike} source The current projection. Can be a + * string identifier or a {@link ol.proj.Projection} object. + * @param {ol.ProjectionLike} destination The desired projection. Can be a + * string identifier or a {@link ol.proj.Projection} object. + * @return {ol.geom.Geometry} This geometry. Note that original geometry is + * modified in place. + * @api stable + */ +ol.geom.Geometry.prototype.transform = function(source, destination) { + goog.asserts.assert( + ol.proj.get(source).getUnits() !== ol.proj.Units.TILE_PIXELS && + ol.proj.get(destination).getUnits() !== ol.proj.Units.TILE_PIXELS, + 'cannot transform geometries with TILE_PIXELS units'); + this.applyTransform(ol.proj.getTransform(source, destination)); + return this; +}; + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Supplies a Float32Array implementation that implements + * most of the Float32Array spec and that can be used when a built-in + * implementation is not available. + * + * Note that if no existing Float32Array implementation is found then + * this class and all its public properties are exported as Float32Array. + * + * Adding support for the other TypedArray classes here does not make sense + * since this vector math library only needs Float32Array. + * + */ +goog.provide('goog.vec.Float32Array'); + + + +/** + * Constructs a new Float32Array. The new array is initialized to all zeros. + * + * @param {goog.vec.Float32Array|Array|ArrayBuffer|number} p0 + * The length of the array, or an array to initialize the contents of the + * new Float32Array. + * @constructor + * @implements {IArrayLike<number>} + * @final + */ +goog.vec.Float32Array = function(p0) { + this.length = /** @type {number} */ (p0.length || p0); + for (var i = 0; i < this.length; i++) { + this[i] = p0[i] || 0; + } +}; + + +/** + * The number of bytes in an element (as defined by the Typed Array + * specification). + * + * @type {number} + */ +goog.vec.Float32Array.BYTES_PER_ELEMENT = 4; + + +/** + * The number of bytes in an element (as defined by the Typed Array + * specification). + * + * @type {number} + */ +goog.vec.Float32Array.prototype.BYTES_PER_ELEMENT = 4; + + +/** + * Sets elements of the array. + * @param {Array<number>|Float32Array} values The array of values. + * @param {number=} opt_offset The offset in this array to start. + */ +goog.vec.Float32Array.prototype.set = function(values, opt_offset) { + opt_offset = opt_offset || 0; + for (var i = 0; i < values.length && opt_offset + i < this.length; i++) { + this[opt_offset + i] = values[i]; + } +}; + + +/** + * Creates a string representation of this array. + * @return {string} The string version of this array. + * @override + */ +goog.vec.Float32Array.prototype.toString = Array.prototype.join; + + +/** + * Note that we cannot implement the subarray() or (deprecated) slice() + * methods properly since doing so would require being able to overload + * the [] operator which is not possible in javascript. So we leave + * them unimplemented. Any attempt to call these methods will just result + * in a javascript error since we leave them undefined. + */ + + +/** + * If no existing Float32Array implementation is found then we export + * goog.vec.Float32Array as Float32Array. + */ +if (typeof Float32Array == 'undefined') { + goog.exportProperty( + goog.vec.Float32Array, 'BYTES_PER_ELEMENT', + goog.vec.Float32Array.BYTES_PER_ELEMENT); + goog.exportProperty( + goog.vec.Float32Array.prototype, 'BYTES_PER_ELEMENT', + goog.vec.Float32Array.prototype.BYTES_PER_ELEMENT); + goog.exportProperty( + goog.vec.Float32Array.prototype, 'set', + goog.vec.Float32Array.prototype.set); + goog.exportProperty( + goog.vec.Float32Array.prototype, 'toString', + goog.vec.Float32Array.prototype.toString); + goog.exportSymbol('Float32Array', goog.vec.Float32Array); +} + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Supplies a Float64Array implementation that implements + * most of the Float64Array spec and that can be used when a built-in + * implementation is not available. + * + * Note that if no existing Float64Array implementation is found then this + * class and all its public properties are exported as Float64Array. + * + * Adding support for the other TypedArray classes here does not make sense + * since this vector math library only needs Float32Array and Float64Array. + * + */ +goog.provide('goog.vec.Float64Array'); + + + +/** + * Constructs a new Float64Array. The new array is initialized to all zeros. + * + * @param {goog.vec.Float64Array|Array|ArrayBuffer|number} p0 + * The length of the array, or an array to initialize the contents of the + * new Float64Array. + * @constructor + * @implements {IArrayLike<number>} + * @final + */ +goog.vec.Float64Array = function(p0) { + this.length = /** @type {number} */ (p0.length || p0); + for (var i = 0; i < this.length; i++) { + this[i] = p0[i] || 0; + } +}; + + +/** + * The number of bytes in an element (as defined by the Typed Array + * specification). + * + * @type {number} + */ +goog.vec.Float64Array.BYTES_PER_ELEMENT = 8; + + +/** + * The number of bytes in an element (as defined by the Typed Array + * specification). + * + * @type {number} + */ +goog.vec.Float64Array.prototype.BYTES_PER_ELEMENT = 8; + + +/** + * Sets elements of the array. + * @param {Array<number>|Float64Array} values The array of values. + * @param {number=} opt_offset The offset in this array to start. + */ +goog.vec.Float64Array.prototype.set = function(values, opt_offset) { + opt_offset = opt_offset || 0; + for (var i = 0; i < values.length && opt_offset + i < this.length; i++) { + this[opt_offset + i] = values[i]; + } +}; + + +/** + * Creates a string representation of this array. + * @return {string} The string version of this array. + * @override + */ +goog.vec.Float64Array.prototype.toString = Array.prototype.join; + + +/** + * Note that we cannot implement the subarray() or (deprecated) slice() + * methods properly since doing so would require being able to overload + * the [] operator which is not possible in javascript. So we leave + * them unimplemented. Any attempt to call these methods will just result + * in a javascript error since we leave them undefined. + */ + + +/** + * If no existing Float64Array implementation is found then we export + * goog.vec.Float64Array as Float64Array. + */ +if (typeof Float64Array == 'undefined') { + try { + goog.exportProperty( + goog.vec.Float64Array, 'BYTES_PER_ELEMENT', + goog.vec.Float64Array.BYTES_PER_ELEMENT); + } catch (float64ArrayError) { + // Do nothing. This code is in place to fix b/7225850, in which an error + // is incorrectly thrown for Google TV on an old Chrome. + // TODO(user): remove after that version is retired. + } + + goog.exportProperty( + goog.vec.Float64Array.prototype, 'BYTES_PER_ELEMENT', + goog.vec.Float64Array.prototype.BYTES_PER_ELEMENT); + goog.exportProperty( + goog.vec.Float64Array.prototype, 'set', + goog.vec.Float64Array.prototype.set); + goog.exportProperty( + goog.vec.Float64Array.prototype, 'toString', + goog.vec.Float64Array.prototype.toString); + goog.exportSymbol('Float64Array', goog.vec.Float64Array); +} + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Supplies global data types and constants for the vector math + * library. + */ +goog.provide('goog.vec'); +goog.provide('goog.vec.AnyType'); +goog.provide('goog.vec.ArrayType'); +goog.provide('goog.vec.Float32'); +goog.provide('goog.vec.Float64'); +goog.provide('goog.vec.Number'); + + +/** + * On platforms that don't have native Float32Array or Float64Array support we + * use a javascript implementation so that this math library can be used on all + * platforms. + * @suppress {extraRequire} + */ +goog.require('goog.vec.Float32Array'); +/** @suppress {extraRequire} */ +goog.require('goog.vec.Float64Array'); + +// All vector and matrix operations are based upon arrays of numbers using +// either Float32Array, Float64Array, or a standard Javascript Array of +// Numbers. + + +/** @typedef {!Float32Array} */ +goog.vec.Float32; + + +/** @typedef {!Float64Array} */ +goog.vec.Float64; + + +/** @typedef {!Array<number>} */ +goog.vec.Number; + + +/** @typedef {!goog.vec.Float32|!goog.vec.Float64|!goog.vec.Number} */ +goog.vec.AnyType; + + +/** + * @deprecated Use AnyType. + * @typedef {!Float32Array|!Array<number>} + */ +goog.vec.ArrayType; + + +/** + * For graphics work, 6 decimal places of accuracy are typically all that is + * required. + * + * @type {number} + * @const + */ +goog.vec.EPSILON = 1e-6; + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Supplies 3 element vectors that are compatible with WebGL. + * Each element is a float32 since that is typically the desired size of a + * 3-vector in the GPU. The API is structured to avoid unnecessary memory + * allocations. The last parameter will typically be the output vector and + * an object can be both an input and output parameter to all methods except + * where noted. + * + */ +goog.provide('goog.vec.Vec3'); + +/** @suppress {extraRequire} */ +goog.require('goog.vec'); + +/** @typedef {goog.vec.Float32} */ goog.vec.Vec3.Float32; +/** @typedef {goog.vec.Float64} */ goog.vec.Vec3.Float64; +/** @typedef {goog.vec.Number} */ goog.vec.Vec3.Number; +/** @typedef {goog.vec.AnyType} */ goog.vec.Vec3.AnyType; + +// The following two types are deprecated - use the above types instead. +/** @typedef {Float32Array} */ goog.vec.Vec3.Type; +/** @typedef {goog.vec.ArrayType} */ goog.vec.Vec3.Vec3Like; + + +/** + * Creates a 3 element vector of Float32. The array is initialized to zero. + * + * @return {!goog.vec.Vec3.Float32} The new 3 element array. + */ +goog.vec.Vec3.createFloat32 = function() { + return new Float32Array(3); +}; + + +/** + * Creates a 3 element vector of Float64. The array is initialized to zero. + * + * @return {!goog.vec.Vec3.Float64} The new 3 element array. + */ +goog.vec.Vec3.createFloat64 = function() { + return new Float64Array(3); +}; + + +/** + * Creates a 3 element vector of Number. The array is initialized to zero. + * + * @return {!goog.vec.Vec3.Number} The new 3 element array. + */ +goog.vec.Vec3.createNumber = function() { + var a = new Array(3); + goog.vec.Vec3.setFromValues(a, 0, 0, 0); + return a; +}; + + +/** + * Creates a 3 element vector of Float32Array. The array is initialized to zero. + * + * @deprecated Use createFloat32. + * @return {!goog.vec.Vec3.Type} The new 3 element array. + */ +goog.vec.Vec3.create = function() { + return new Float32Array(3); +}; + + +/** + * Creates a new 3 element Float32 vector initialized with the value from the + * given array. + * + * @param {goog.vec.Vec3.AnyType} vec The source 3 element array. + * @return {!goog.vec.Vec3.Float32} The new 3 element array. + */ +goog.vec.Vec3.createFloat32FromArray = function(vec) { + var newVec = goog.vec.Vec3.createFloat32(); + goog.vec.Vec3.setFromArray(newVec, vec); + return newVec; +}; + + +/** + * Creates a new 3 element Float32 vector initialized with the supplied values. + * + * @param {number} v0 The value for element at index 0. + * @param {number} v1 The value for element at index 1. + * @param {number} v2 The value for element at index 2. + * @return {!goog.vec.Vec3.Float32} The new vector. + */ +goog.vec.Vec3.createFloat32FromValues = function(v0, v1, v2) { + var a = goog.vec.Vec3.createFloat32(); + goog.vec.Vec3.setFromValues(a, v0, v1, v2); + return a; +}; + + +/** + * Creates a clone of the given 3 element Float32 vector. + * + * @param {goog.vec.Vec3.Float32} vec The source 3 element vector. + * @return {!goog.vec.Vec3.Float32} The new cloned vector. + */ +goog.vec.Vec3.cloneFloat32 = goog.vec.Vec3.createFloat32FromArray; + + +/** + * Creates a new 3 element Float64 vector initialized with the value from the + * given array. + * + * @param {goog.vec.Vec3.AnyType} vec The source 3 element array. + * @return {!goog.vec.Vec3.Float64} The new 3 element array. + */ +goog.vec.Vec3.createFloat64FromArray = function(vec) { + var newVec = goog.vec.Vec3.createFloat64(); + goog.vec.Vec3.setFromArray(newVec, vec); + return newVec; +}; + + +/** +* Creates a new 3 element Float64 vector initialized with the supplied values. +* +* @param {number} v0 The value for element at index 0. +* @param {number} v1 The value for element at index 1. +* @param {number} v2 The value for element at index 2. +* @return {!goog.vec.Vec3.Float64} The new vector. +*/ +goog.vec.Vec3.createFloat64FromValues = function(v0, v1, v2) { + var vec = goog.vec.Vec3.createFloat64(); + goog.vec.Vec3.setFromValues(vec, v0, v1, v2); + return vec; +}; + + +/** + * Creates a clone of the given 3 element vector. + * + * @param {goog.vec.Vec3.Float64} vec The source 3 element vector. + * @return {!goog.vec.Vec3.Float64} The new cloned vector. + */ +goog.vec.Vec3.cloneFloat64 = goog.vec.Vec3.createFloat64FromArray; + + +/** + * Creates a new 3 element vector initialized with the value from the given + * array. + * + * @deprecated Use createFloat32FromArray. + * @param {goog.vec.Vec3.Vec3Like} vec The source 3 element array. + * @return {!goog.vec.Vec3.Type} The new 3 element array. + */ +goog.vec.Vec3.createFromArray = function(vec) { + var newVec = goog.vec.Vec3.create(); + goog.vec.Vec3.setFromArray(newVec, vec); + return newVec; +}; + + +/** + * Creates a new 3 element vector initialized with the supplied values. + * + * @deprecated Use createFloat32FromValues. + * @param {number} v0 The value for element at index 0. + * @param {number} v1 The value for element at index 1. + * @param {number} v2 The value for element at index 2. + * @return {!goog.vec.Vec3.Type} The new vector. + */ +goog.vec.Vec3.createFromValues = function(v0, v1, v2) { + var vec = goog.vec.Vec3.create(); + goog.vec.Vec3.setFromValues(vec, v0, v1, v2); + return vec; +}; + + +/** + * Creates a clone of the given 3 element vector. + * + * @deprecated Use cloneFloat32. + * @param {goog.vec.Vec3.Vec3Like} vec The source 3 element vector. + * @return {!goog.vec.Vec3.Type} The new cloned vector. + */ +goog.vec.Vec3.clone = function(vec) { + var newVec = goog.vec.Vec3.create(); + goog.vec.Vec3.setFromArray(newVec, vec); + return newVec; +}; + + +/** + * Initializes the vector with the given values. + * + * @param {goog.vec.Vec3.AnyType} vec The vector to receive the values. + * @param {number} v0 The value for element at index 0. + * @param {number} v1 The value for element at index 1. + * @param {number} v2 The value for element at index 2. + * @return {!goog.vec.Vec3.AnyType} Return vec so that operations can be + * chained together. + */ +goog.vec.Vec3.setFromValues = function(vec, v0, v1, v2) { + vec[0] = v0; + vec[1] = v1; + vec[2] = v2; + return vec; +}; + + +/** + * Initializes the vector with the given array of values. + * + * @param {goog.vec.Vec3.AnyType} vec The vector to receive the + * values. + * @param {goog.vec.Vec3.AnyType} values The array of values. + * @return {!goog.vec.Vec3.AnyType} Return vec so that operations can be + * chained together. + */ +goog.vec.Vec3.setFromArray = function(vec, values) { + vec[0] = values[0]; + vec[1] = values[1]; + vec[2] = values[2]; + return vec; +}; + + +/** + * Performs a component-wise addition of vec0 and vec1 together storing the + * result into resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The first addend. + * @param {goog.vec.Vec3.AnyType} vec1 The second addend. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to + * receive the result. May be vec0 or vec1. + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.add = function(vec0, vec1, resultVec) { + resultVec[0] = vec0[0] + vec1[0]; + resultVec[1] = vec0[1] + vec1[1]; + resultVec[2] = vec0[2] + vec1[2]; + return resultVec; +}; + + +/** + * Performs a component-wise subtraction of vec1 from vec0 storing the + * result into resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The minuend. + * @param {goog.vec.Vec3.AnyType} vec1 The subtrahend. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to + * receive the result. May be vec0 or vec1. + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.subtract = function(vec0, vec1, resultVec) { + resultVec[0] = vec0[0] - vec1[0]; + resultVec[1] = vec0[1] - vec1[1]; + resultVec[2] = vec0[2] - vec1[2]; + return resultVec; +}; + + +/** + * Negates vec0, storing the result into resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The vector to negate. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to + * receive the result. May be vec0. + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.negate = function(vec0, resultVec) { + resultVec[0] = -vec0[0]; + resultVec[1] = -vec0[1]; + resultVec[2] = -vec0[2]; + return resultVec; +}; + + +/** + * Takes the absolute value of each component of vec0 storing the result in + * resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The source vector. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the result. + * May be vec0. + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.abs = function(vec0, resultVec) { + resultVec[0] = Math.abs(vec0[0]); + resultVec[1] = Math.abs(vec0[1]); + resultVec[2] = Math.abs(vec0[2]); + return resultVec; +}; + + +/** + * Multiplies each component of vec0 with scalar storing the product into + * resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The source vector. + * @param {number} scalar The value to multiply with each component of vec0. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to + * receive the result. May be vec0. + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.scale = function(vec0, scalar, resultVec) { + resultVec[0] = vec0[0] * scalar; + resultVec[1] = vec0[1] * scalar; + resultVec[2] = vec0[2] * scalar; + return resultVec; +}; + + +/** + * Returns the magnitudeSquared of the given vector. + * + * @param {goog.vec.Vec3.AnyType} vec0 The vector. + * @return {number} The magnitude of the vector. + */ +goog.vec.Vec3.magnitudeSquared = function(vec0) { + var x = vec0[0], y = vec0[1], z = vec0[2]; + return x * x + y * y + z * z; +}; + + +/** + * Returns the magnitude of the given vector. + * + * @param {goog.vec.Vec3.AnyType} vec0 The vector. + * @return {number} The magnitude of the vector. + */ +goog.vec.Vec3.magnitude = function(vec0) { + var x = vec0[0], y = vec0[1], z = vec0[2]; + return Math.sqrt(x * x + y * y + z * z); +}; + + +/** + * Normalizes the given vector storing the result into resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The vector to normalize. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to + * receive the result. May be vec0. + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.normalize = function(vec0, resultVec) { + var ilen = 1 / goog.vec.Vec3.magnitude(vec0); + resultVec[0] = vec0[0] * ilen; + resultVec[1] = vec0[1] * ilen; + resultVec[2] = vec0[2] * ilen; + return resultVec; +}; + + +/** + * Returns the scalar product of vectors v0 and v1. + * + * @param {goog.vec.Vec3.AnyType} v0 The first vector. + * @param {goog.vec.Vec3.AnyType} v1 The second vector. + * @return {number} The scalar product. + */ +goog.vec.Vec3.dot = function(v0, v1) { + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2]; +}; + + +/** + * Computes the vector (cross) product of v0 and v1 storing the result into + * resultVec. + * + * @param {goog.vec.Vec3.AnyType} v0 The first vector. + * @param {goog.vec.Vec3.AnyType} v1 The second vector. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the + * results. May be either v0 or v1. + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.cross = function(v0, v1, resultVec) { + var x0 = v0[0], y0 = v0[1], z0 = v0[2]; + var x1 = v1[0], y1 = v1[1], z1 = v1[2]; + resultVec[0] = y0 * z1 - z0 * y1; + resultVec[1] = z0 * x1 - x0 * z1; + resultVec[2] = x0 * y1 - y0 * x1; + return resultVec; +}; + + +/** + * Returns the squared distance between two points. + * + * @param {goog.vec.Vec3.AnyType} vec0 First point. + * @param {goog.vec.Vec3.AnyType} vec1 Second point. + * @return {number} The squared distance between the points. + */ +goog.vec.Vec3.distanceSquared = function(vec0, vec1) { + var x = vec0[0] - vec1[0]; + var y = vec0[1] - vec1[1]; + var z = vec0[2] - vec1[2]; + return x * x + y * y + z * z; +}; + + +/** + * Returns the distance between two points. + * + * @param {goog.vec.Vec3.AnyType} vec0 First point. + * @param {goog.vec.Vec3.AnyType} vec1 Second point. + * @return {number} The distance between the points. + */ +goog.vec.Vec3.distance = function(vec0, vec1) { + return Math.sqrt(goog.vec.Vec3.distanceSquared(vec0, vec1)); +}; + + +/** + * Returns a unit vector pointing from one point to another. + * If the input points are equal then the result will be all zeros. + * + * @param {goog.vec.Vec3.AnyType} vec0 Origin point. + * @param {goog.vec.Vec3.AnyType} vec1 Target point. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the + * results (may be vec0 or vec1). + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.direction = function(vec0, vec1, resultVec) { + var x = vec1[0] - vec0[0]; + var y = vec1[1] - vec0[1]; + var z = vec1[2] - vec0[2]; + var d = Math.sqrt(x * x + y * y + z * z); + if (d) { + d = 1 / d; + resultVec[0] = x * d; + resultVec[1] = y * d; + resultVec[2] = z * d; + } else { + resultVec[0] = resultVec[1] = resultVec[2] = 0; + } + return resultVec; +}; + + +/** + * Linearly interpolate from vec0 to v1 according to f. The value of f should be + * in the range [0..1] otherwise the results are undefined. + * + * @param {goog.vec.Vec3.AnyType} v0 The first vector. + * @param {goog.vec.Vec3.AnyType} v1 The second vector. + * @param {number} f The interpolation factor. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the + * results (may be v0 or v1). + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.lerp = function(v0, v1, f, resultVec) { + var x = v0[0], y = v0[1], z = v0[2]; + resultVec[0] = (v1[0] - x) * f + x; + resultVec[1] = (v1[1] - y) * f + y; + resultVec[2] = (v1[2] - z) * f + z; + return resultVec; +}; + + +/** + * Compares the components of vec0 with the components of another vector or + * scalar, storing the larger values in resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The source vector. + * @param {goog.vec.Vec3.AnyType|number} limit The limit vector or scalar. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the + * results (may be vec0 or limit). + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.max = function(vec0, limit, resultVec) { + if (goog.isNumber(limit)) { + resultVec[0] = Math.max(vec0[0], limit); + resultVec[1] = Math.max(vec0[1], limit); + resultVec[2] = Math.max(vec0[2], limit); + } else { + resultVec[0] = Math.max(vec0[0], limit[0]); + resultVec[1] = Math.max(vec0[1], limit[1]); + resultVec[2] = Math.max(vec0[2], limit[2]); + } + return resultVec; +}; + + +/** + * Compares the components of vec0 with the components of another vector or + * scalar, storing the smaller values in resultVec. + * + * @param {goog.vec.Vec3.AnyType} vec0 The source vector. + * @param {goog.vec.Vec3.AnyType|number} limit The limit vector or scalar. + * @param {goog.vec.Vec3.AnyType} resultVec The vector to receive the + * results (may be vec0 or limit). + * @return {!goog.vec.Vec3.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec3.min = function(vec0, limit, resultVec) { + if (goog.isNumber(limit)) { + resultVec[0] = Math.min(vec0[0], limit); + resultVec[1] = Math.min(vec0[1], limit); + resultVec[2] = Math.min(vec0[2], limit); + } else { + resultVec[0] = Math.min(vec0[0], limit[0]); + resultVec[1] = Math.min(vec0[1], limit[1]); + resultVec[2] = Math.min(vec0[2], limit[2]); + } + return resultVec; +}; + + +/** + * Returns true if the components of v0 are equal to the components of v1. + * + * @param {goog.vec.Vec3.AnyType} v0 The first vector. + * @param {goog.vec.Vec3.AnyType} v1 The second vector. + * @return {boolean} True if the vectors are equal, false otherwise. + */ +goog.vec.Vec3.equals = function(v0, v1) { + return v0.length == v1.length && v0[0] == v1[0] && v0[1] == v1[1] && + v0[2] == v1[2]; +}; + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Supplies 4 element vectors that are compatible with WebGL. + * Each element is a float32 since that is typically the desired size of a + * 4-vector in the GPU. The API is structured to avoid unnecessary memory + * allocations. The last parameter will typically be the output vector and + * an object can be both an input and output parameter to all methods except + * where noted. + * + */ +goog.provide('goog.vec.Vec4'); + +/** @suppress {extraRequire} */ +goog.require('goog.vec'); + +/** @typedef {goog.vec.Float32} */ goog.vec.Vec4.Float32; +/** @typedef {goog.vec.Float64} */ goog.vec.Vec4.Float64; +/** @typedef {goog.vec.Number} */ goog.vec.Vec4.Number; +/** @typedef {goog.vec.AnyType} */ goog.vec.Vec4.AnyType; + +// The following two types are deprecated - use the above types instead. +/** @typedef {Float32Array} */ goog.vec.Vec4.Type; +/** @typedef {goog.vec.ArrayType} */ goog.vec.Vec4.Vec4Like; + + +/** + * Creates a 4 element vector of Float32. The array is initialized to zero. + * + * @return {!goog.vec.Vec4.Float32} The new 3 element array. + */ +goog.vec.Vec4.createFloat32 = function() { + return new Float32Array(4); +}; + + +/** + * Creates a 4 element vector of Float64. The array is initialized to zero. + * + * @return {!goog.vec.Vec4.Float64} The new 4 element array. + */ +goog.vec.Vec4.createFloat64 = function() { + return new Float64Array(4); +}; + + +/** + * Creates a 4 element vector of Number. The array is initialized to zero. + * + * @return {!goog.vec.Vec4.Number} The new 4 element array. + */ +goog.vec.Vec4.createNumber = function() { + var v = new Array(4); + goog.vec.Vec4.setFromValues(v, 0, 0, 0, 0); + return v; +}; + + +/** + * Creates a 4 element vector of Float32Array. The array is initialized to zero. + * + * @deprecated Use createFloat32. + * @return {!goog.vec.Vec4.Type} The new 4 element array. + */ +goog.vec.Vec4.create = function() { + return new Float32Array(4); +}; + + +/** + * Creates a new 4 element vector initialized with the value from the given + * array. + * + * @deprecated Use createFloat32FromArray. + * @param {goog.vec.Vec4.Vec4Like} vec The source 4 element array. + * @return {!goog.vec.Vec4.Type} The new 4 element array. + */ +goog.vec.Vec4.createFromArray = function(vec) { + var newVec = goog.vec.Vec4.create(); + goog.vec.Vec4.setFromArray(newVec, vec); + return newVec; +}; + + +/** + * Creates a new 4 element FLoat32 vector initialized with the value from the + * given array. + * + * @param {goog.vec.Vec4.AnyType} vec The source 3 element array. + * @return {!goog.vec.Vec4.Float32} The new 3 element array. + */ +goog.vec.Vec4.createFloat32FromArray = function(vec) { + var newVec = goog.vec.Vec4.createFloat32(); + goog.vec.Vec4.setFromArray(newVec, vec); + return newVec; +}; + + +/** + * Creates a new 4 element Float32 vector initialized with the supplied values. + * + * @param {number} v0 The value for element at index 0. + * @param {number} v1 The value for element at index 1. + * @param {number} v2 The value for element at index 2. + * @param {number} v3 The value for element at index 3. + * @return {!goog.vec.Vec4.Float32} The new vector. + */ +goog.vec.Vec4.createFloat32FromValues = function(v0, v1, v2, v3) { + var vec = goog.vec.Vec4.createFloat32(); + goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3); + return vec; +}; + + +/** + * Creates a clone of the given 4 element Float32 vector. + * + * @param {goog.vec.Vec4.Float32} vec The source 3 element vector. + * @return {!goog.vec.Vec4.Float32} The new cloned vector. + */ +goog.vec.Vec4.cloneFloat32 = goog.vec.Vec4.createFloat32FromArray; + + +/** + * Creates a new 4 element Float64 vector initialized with the value from the + * given array. + * + * @param {goog.vec.Vec4.AnyType} vec The source 4 element array. + * @return {!goog.vec.Vec4.Float64} The new 4 element array. + */ +goog.vec.Vec4.createFloat64FromArray = function(vec) { + var newVec = goog.vec.Vec4.createFloat64(); + goog.vec.Vec4.setFromArray(newVec, vec); + return newVec; +}; + + +/** +* Creates a new 4 element Float64 vector initialized with the supplied values. +* +* @param {number} v0 The value for element at index 0. +* @param {number} v1 The value for element at index 1. +* @param {number} v2 The value for element at index 2. +* @param {number} v3 The value for element at index 3. +* @return {!goog.vec.Vec4.Float64} The new vector. +*/ +goog.vec.Vec4.createFloat64FromValues = function(v0, v1, v2, v3) { + var vec = goog.vec.Vec4.createFloat64(); + goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3); + return vec; +}; + + +/** + * Creates a clone of the given 4 element vector. + * + * @param {goog.vec.Vec4.Float64} vec The source 4 element vector. + * @return {!goog.vec.Vec4.Float64} The new cloned vector. + */ +goog.vec.Vec4.cloneFloat64 = goog.vec.Vec4.createFloat64FromArray; + + +/** + * Creates a new 4 element vector initialized with the supplied values. + * + * @deprecated Use createFloat32FromValues. + * @param {number} v0 The value for element at index 0. + * @param {number} v1 The value for element at index 1. + * @param {number} v2 The value for element at index 2. + * @param {number} v3 The value for element at index 3. + * @return {!goog.vec.Vec4.Type} The new vector. + */ +goog.vec.Vec4.createFromValues = function(v0, v1, v2, v3) { + var vec = goog.vec.Vec4.create(); + goog.vec.Vec4.setFromValues(vec, v0, v1, v2, v3); + return vec; +}; + + +/** + * Creates a clone of the given 4 element vector. + * + * @deprecated Use cloneFloat32. + * @param {goog.vec.Vec4.Vec4Like} vec The source 4 element vector. + * @return {!goog.vec.Vec4.Type} The new cloned vector. + */ +goog.vec.Vec4.clone = goog.vec.Vec4.createFromArray; + + +/** + * Initializes the vector with the given values. + * + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the values. + * @param {number} v0 The value for element at index 0. + * @param {number} v1 The value for element at index 1. + * @param {number} v2 The value for element at index 2. + * @param {number} v3 The value for element at index 3. + * @return {!goog.vec.Vec4.AnyType} Return vec so that operations can be + * chained together. + */ +goog.vec.Vec4.setFromValues = function(vec, v0, v1, v2, v3) { + vec[0] = v0; + vec[1] = v1; + vec[2] = v2; + vec[3] = v3; + return vec; +}; + + +/** + * Initializes the vector with the given array of values. + * + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the + * values. + * @param {goog.vec.Vec4.AnyType} values The array of values. + * @return {!goog.vec.Vec4.AnyType} Return vec so that operations can be + * chained together. + */ +goog.vec.Vec4.setFromArray = function(vec, values) { + vec[0] = values[0]; + vec[1] = values[1]; + vec[2] = values[2]; + vec[3] = values[3]; + return vec; +}; + + +/** + * Performs a component-wise addition of vec0 and vec1 together storing the + * result into resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The first addend. + * @param {goog.vec.Vec4.AnyType} vec1 The second addend. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to + * receive the result. May be vec0 or vec1. + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.add = function(vec0, vec1, resultVec) { + resultVec[0] = vec0[0] + vec1[0]; + resultVec[1] = vec0[1] + vec1[1]; + resultVec[2] = vec0[2] + vec1[2]; + resultVec[3] = vec0[3] + vec1[3]; + return resultVec; +}; + + +/** + * Performs a component-wise subtraction of vec1 from vec0 storing the + * result into resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The minuend. + * @param {goog.vec.Vec4.AnyType} vec1 The subtrahend. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to + * receive the result. May be vec0 or vec1. + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.subtract = function(vec0, vec1, resultVec) { + resultVec[0] = vec0[0] - vec1[0]; + resultVec[1] = vec0[1] - vec1[1]; + resultVec[2] = vec0[2] - vec1[2]; + resultVec[3] = vec0[3] - vec1[3]; + return resultVec; +}; + + +/** + * Negates vec0, storing the result into resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The vector to negate. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to + * receive the result. May be vec0. + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.negate = function(vec0, resultVec) { + resultVec[0] = -vec0[0]; + resultVec[1] = -vec0[1]; + resultVec[2] = -vec0[2]; + resultVec[3] = -vec0[3]; + return resultVec; +}; + + +/** + * Takes the absolute value of each component of vec0 storing the result in + * resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The source vector. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the result. + * May be vec0. + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.abs = function(vec0, resultVec) { + resultVec[0] = Math.abs(vec0[0]); + resultVec[1] = Math.abs(vec0[1]); + resultVec[2] = Math.abs(vec0[2]); + resultVec[3] = Math.abs(vec0[3]); + return resultVec; +}; + + +/** + * Multiplies each component of vec0 with scalar storing the product into + * resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The source vector. + * @param {number} scalar The value to multiply with each component of vec0. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to + * receive the result. May be vec0. + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.scale = function(vec0, scalar, resultVec) { + resultVec[0] = vec0[0] * scalar; + resultVec[1] = vec0[1] * scalar; + resultVec[2] = vec0[2] * scalar; + resultVec[3] = vec0[3] * scalar; + return resultVec; +}; + + +/** + * Returns the magnitudeSquared of the given vector. + * + * @param {goog.vec.Vec4.AnyType} vec0 The vector. + * @return {number} The magnitude of the vector. + */ +goog.vec.Vec4.magnitudeSquared = function(vec0) { + var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3]; + return x * x + y * y + z * z + w * w; +}; + + +/** + * Returns the magnitude of the given vector. + * + * @param {goog.vec.Vec4.AnyType} vec0 The vector. + * @return {number} The magnitude of the vector. + */ +goog.vec.Vec4.magnitude = function(vec0) { + var x = vec0[0], y = vec0[1], z = vec0[2], w = vec0[3]; + return Math.sqrt(x * x + y * y + z * z + w * w); +}; + + +/** + * Normalizes the given vector storing the result into resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The vector to normalize. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to + * receive the result. May be vec0. + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.normalize = function(vec0, resultVec) { + var ilen = 1 / goog.vec.Vec4.magnitude(vec0); + resultVec[0] = vec0[0] * ilen; + resultVec[1] = vec0[1] * ilen; + resultVec[2] = vec0[2] * ilen; + resultVec[3] = vec0[3] * ilen; + return resultVec; +}; + + +/** + * Returns the scalar product of vectors v0 and v1. + * + * @param {goog.vec.Vec4.AnyType} v0 The first vector. + * @param {goog.vec.Vec4.AnyType} v1 The second vector. + * @return {number} The scalar product. + */ +goog.vec.Vec4.dot = function(v0, v1) { + return v0[0] * v1[0] + v0[1] * v1[1] + v0[2] * v1[2] + v0[3] * v1[3]; +}; + + +/** + * Linearly interpolate from v0 to v1 according to f. The value of f should be + * in the range [0..1] otherwise the results are undefined. + * + * @param {goog.vec.Vec4.AnyType} v0 The first vector. + * @param {goog.vec.Vec4.AnyType} v1 The second vector. + * @param {number} f The interpolation factor. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the + * results (may be v0 or v1). + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.lerp = function(v0, v1, f, resultVec) { + var x = v0[0], y = v0[1], z = v0[2], w = v0[3]; + resultVec[0] = (v1[0] - x) * f + x; + resultVec[1] = (v1[1] - y) * f + y; + resultVec[2] = (v1[2] - z) * f + z; + resultVec[3] = (v1[3] - w) * f + w; + return resultVec; +}; + + +/** + * Compares the components of vec0 with the components of another vector or + * scalar, storing the larger values in resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The source vector. + * @param {goog.vec.Vec4.AnyType|number} limit The limit vector or scalar. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the + * results (may be vec0 or limit). + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.max = function(vec0, limit, resultVec) { + if (goog.isNumber(limit)) { + resultVec[0] = Math.max(vec0[0], limit); + resultVec[1] = Math.max(vec0[1], limit); + resultVec[2] = Math.max(vec0[2], limit); + resultVec[3] = Math.max(vec0[3], limit); + } else { + resultVec[0] = Math.max(vec0[0], limit[0]); + resultVec[1] = Math.max(vec0[1], limit[1]); + resultVec[2] = Math.max(vec0[2], limit[2]); + resultVec[3] = Math.max(vec0[3], limit[3]); + } + return resultVec; +}; + + +/** + * Compares the components of vec0 with the components of another vector or + * scalar, storing the smaller values in resultVec. + * + * @param {goog.vec.Vec4.AnyType} vec0 The source vector. + * @param {goog.vec.Vec4.AnyType|number} limit The limit vector or scalar. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to receive the + * results (may be vec0 or limit). + * @return {!goog.vec.Vec4.AnyType} Return resultVec so that operations can be + * chained together. + */ +goog.vec.Vec4.min = function(vec0, limit, resultVec) { + if (goog.isNumber(limit)) { + resultVec[0] = Math.min(vec0[0], limit); + resultVec[1] = Math.min(vec0[1], limit); + resultVec[2] = Math.min(vec0[2], limit); + resultVec[3] = Math.min(vec0[3], limit); + } else { + resultVec[0] = Math.min(vec0[0], limit[0]); + resultVec[1] = Math.min(vec0[1], limit[1]); + resultVec[2] = Math.min(vec0[2], limit[2]); + resultVec[3] = Math.min(vec0[3], limit[3]); + } + return resultVec; +}; + + +/** + * Returns true if the components of v0 are equal to the components of v1. + * + * @param {goog.vec.Vec4.AnyType} v0 The first vector. + * @param {goog.vec.Vec4.AnyType} v1 The second vector. + * @return {boolean} True if the vectors are equal, false otherwise. + */ +goog.vec.Vec4.equals = function(v0, v1) { + return v0.length == v1.length && v0[0] == v1[0] && v0[1] == v1[1] && + v0[2] == v1[2] && v0[3] == v1[3]; +}; + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Implements 4x4 matrices and their related functions which are + * compatible with WebGL. The API is structured to avoid unnecessary memory + * allocations. The last parameter will typically be the output vector and + * an object can be both an input and output parameter to all methods except + * where noted. Matrix operations follow the mathematical form when multiplying + * vectors as follows: resultVec = matrix * vec. + * + * The matrices are stored in column-major order. + * + */ +goog.provide('goog.vec.Mat4'); + +goog.require('goog.vec'); +goog.require('goog.vec.Vec3'); +goog.require('goog.vec.Vec4'); + + +/** @typedef {goog.vec.Float32} */ goog.vec.Mat4.Float32; +/** @typedef {goog.vec.Float64} */ goog.vec.Mat4.Float64; +/** @typedef {goog.vec.Number} */ goog.vec.Mat4.Number; +/** @typedef {goog.vec.AnyType} */ goog.vec.Mat4.AnyType; + +// The following two types are deprecated - use the above types instead. +/** @typedef {!Float32Array} */ goog.vec.Mat4.Type; +/** @typedef {goog.vec.ArrayType} */ goog.vec.Mat4.Mat4Like; + + +/** + * Creates the array representation of a 4x4 matrix of Float32. + * The use of the array directly instead of a class reduces overhead. + * The returned matrix is cleared to all zeros. + * + * @return {!goog.vec.Mat4.Float32} The new matrix. + */ +goog.vec.Mat4.createFloat32 = function() { + return new Float32Array(16); +}; + + +/** + * Creates the array representation of a 4x4 matrix of Float64. + * The returned matrix is cleared to all zeros. + * + * @return {!goog.vec.Mat4.Float64} The new matrix. + */ +goog.vec.Mat4.createFloat64 = function() { + return new Float64Array(16); +}; + + +/** + * Creates the array representation of a 4x4 matrix of Number. + * The returned matrix is cleared to all zeros. + * + * @return {!goog.vec.Mat4.Number} The new matrix. + */ +goog.vec.Mat4.createNumber = function() { + var a = new Array(16); + goog.vec.Mat4.setFromValues( + a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + return a; +}; + + +/** + * Creates the array representation of a 4x4 matrix of Float32. + * The returned matrix is cleared to all zeros. + * + * @deprecated Use createFloat32. + * @return {!goog.vec.Mat4.Type} The new matrix. + */ +goog.vec.Mat4.create = function() { + return goog.vec.Mat4.createFloat32(); +}; + + +/** + * Creates a 4x4 identity matrix of Float32. + * + * @return {!goog.vec.Mat4.Float32} The new 16 element array. + */ +goog.vec.Mat4.createFloat32Identity = function() { + var mat = goog.vec.Mat4.createFloat32(); + mat[0] = mat[5] = mat[10] = mat[15] = 1; + return mat; +}; + + +/** + * Creates a 4x4 identity matrix of Float64. + * + * @return {!goog.vec.Mat4.Float64} The new 16 element array. + */ +goog.vec.Mat4.createFloat64Identity = function() { + var mat = goog.vec.Mat4.createFloat64(); + mat[0] = mat[5] = mat[10] = mat[15] = 1; + return mat; +}; + + +/** + * Creates a 4x4 identity matrix of Number. + * The returned matrix is cleared to all zeros. + * + * @return {!goog.vec.Mat4.Number} The new 16 element array. + */ +goog.vec.Mat4.createNumberIdentity = function() { + var a = new Array(16); + goog.vec.Mat4.setFromValues( + a, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return a; +}; + + +/** + * Creates the array representation of a 4x4 matrix of Float32. + * The returned matrix is cleared to all zeros. + * + * @deprecated Use createFloat32Identity. + * @return {!goog.vec.Mat4.Type} The new 16 element array. + */ +goog.vec.Mat4.createIdentity = function() { + return goog.vec.Mat4.createFloat32Identity(); +}; + + +/** + * Creates a 4x4 matrix of Float32 initialized from the given array. + * + * @param {goog.vec.Mat4.AnyType} matrix The array containing the + * matrix values in column major order. + * @return {!goog.vec.Mat4.Float32} The new, 16 element array. + */ +goog.vec.Mat4.createFloat32FromArray = function(matrix) { + var newMatrix = goog.vec.Mat4.createFloat32(); + goog.vec.Mat4.setFromArray(newMatrix, matrix); + return newMatrix; +}; + + +/** + * Creates a 4x4 matrix of Float32 initialized from the given values. + * + * @param {number} v00 The values at (0, 0). + * @param {number} v10 The values at (1, 0). + * @param {number} v20 The values at (2, 0). + * @param {number} v30 The values at (3, 0). + * @param {number} v01 The values at (0, 1). + * @param {number} v11 The values at (1, 1). + * @param {number} v21 The values at (2, 1). + * @param {number} v31 The values at (3, 1). + * @param {number} v02 The values at (0, 2). + * @param {number} v12 The values at (1, 2). + * @param {number} v22 The values at (2, 2). + * @param {number} v32 The values at (3, 2). + * @param {number} v03 The values at (0, 3). + * @param {number} v13 The values at (1, 3). + * @param {number} v23 The values at (2, 3). + * @param {number} v33 The values at (3, 3). + * @return {!goog.vec.Mat4.Float32} The new, 16 element array. + */ +goog.vec.Mat4.createFloat32FromValues = function( + v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32, v03, v13, v23, + v33) { + var newMatrix = goog.vec.Mat4.createFloat32(); + goog.vec.Mat4.setFromValues( + newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32, + v03, v13, v23, v33); + return newMatrix; +}; + + +/** + * Creates a clone of a 4x4 matrix of Float32. + * + * @param {goog.vec.Mat4.Float32} matrix The source 4x4 matrix. + * @return {!goog.vec.Mat4.Float32} The new 4x4 element matrix. + */ +goog.vec.Mat4.cloneFloat32 = goog.vec.Mat4.createFloat32FromArray; + + +/** + * Creates a 4x4 matrix of Float64 initialized from the given array. + * + * @param {goog.vec.Mat4.AnyType} matrix The array containing the + * matrix values in column major order. + * @return {!goog.vec.Mat4.Float64} The new, nine element array. + */ +goog.vec.Mat4.createFloat64FromArray = function(matrix) { + var newMatrix = goog.vec.Mat4.createFloat64(); + goog.vec.Mat4.setFromArray(newMatrix, matrix); + return newMatrix; +}; + + +/** + * Creates a 4x4 matrix of Float64 initialized from the given values. + * + * @param {number} v00 The values at (0, 0). + * @param {number} v10 The values at (1, 0). + * @param {number} v20 The values at (2, 0). + * @param {number} v30 The values at (3, 0). + * @param {number} v01 The values at (0, 1). + * @param {number} v11 The values at (1, 1). + * @param {number} v21 The values at (2, 1). + * @param {number} v31 The values at (3, 1). + * @param {number} v02 The values at (0, 2). + * @param {number} v12 The values at (1, 2). + * @param {number} v22 The values at (2, 2). + * @param {number} v32 The values at (3, 2). + * @param {number} v03 The values at (0, 3). + * @param {number} v13 The values at (1, 3). + * @param {number} v23 The values at (2, 3). + * @param {number} v33 The values at (3, 3). + * @return {!goog.vec.Mat4.Float64} The new, 16 element array. + */ +goog.vec.Mat4.createFloat64FromValues = function( + v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32, v03, v13, v23, + v33) { + var newMatrix = goog.vec.Mat4.createFloat64(); + goog.vec.Mat4.setFromValues( + newMatrix, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32, + v03, v13, v23, v33); + return newMatrix; +}; + + +/** + * Creates a clone of a 4x4 matrix of Float64. + * + * @param {goog.vec.Mat4.Float64} matrix The source 4x4 matrix. + * @return {!goog.vec.Mat4.Float64} The new 4x4 element matrix. + */ +goog.vec.Mat4.cloneFloat64 = goog.vec.Mat4.createFloat64FromArray; + + +/** + * Creates a 4x4 matrix of Float32 initialized from the given array. + * + * @deprecated Use createFloat32FromArray. + * @param {goog.vec.Mat4.Mat4Like} matrix The array containing the + * matrix values in column major order. + * @return {!goog.vec.Mat4.Type} The new, nine element array. + */ +goog.vec.Mat4.createFromArray = function(matrix) { + var newMatrix = goog.vec.Mat4.createFloat32(); + goog.vec.Mat4.setFromArray(newMatrix, matrix); + return newMatrix; +}; + + +/** + * Creates a 4x4 matrix of Float32 initialized from the given values. + * + * @deprecated Use createFloat32FromValues. + * @param {number} v00 The values at (0, 0). + * @param {number} v10 The values at (1, 0). + * @param {number} v20 The values at (2, 0). + * @param {number} v30 The values at (3, 0). + * @param {number} v01 The values at (0, 1). + * @param {number} v11 The values at (1, 1). + * @param {number} v21 The values at (2, 1). + * @param {number} v31 The values at (3, 1). + * @param {number} v02 The values at (0, 2). + * @param {number} v12 The values at (1, 2). + * @param {number} v22 The values at (2, 2). + * @param {number} v32 The values at (3, 2). + * @param {number} v03 The values at (0, 3). + * @param {number} v13 The values at (1, 3). + * @param {number} v23 The values at (2, 3). + * @param {number} v33 The values at (3, 3). + * @return {!goog.vec.Mat4.Type} The new, 16 element array. + */ +goog.vec.Mat4.createFromValues = function( + v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32, v03, v13, v23, + v33) { + return goog.vec.Mat4.createFloat32FromValues( + v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32, v03, v13, v23, + v33); +}; + + +/** + * Creates a clone of a 4x4 matrix of Float32. + * + * @deprecated Use cloneFloat32. + * @param {goog.vec.Mat4.Mat4Like} matrix The source 4x4 matrix. + * @return {!goog.vec.Mat4.Type} The new 4x4 element matrix. + */ +goog.vec.Mat4.clone = goog.vec.Mat4.createFromArray; + + +/** + * Retrieves the element at the requested row and column. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix containing the + * value to retrieve. + * @param {number} row The row index. + * @param {number} column The column index. + * @return {number} The element value at the requested row, column indices. + */ +goog.vec.Mat4.getElement = function(mat, row, column) { + return mat[row + column * 4]; +}; + + +/** + * Sets the element at the requested row and column. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to set the value on. + * @param {number} row The row index. + * @param {number} column The column index. + * @param {number} value The value to set at the requested row, column. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setElement = function(mat, row, column, value) { + mat[row + column * 4] = value; + return mat; +}; + + +/** + * Initializes the matrix from the set of values. Note the values supplied are + * in column major order. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the + * values. + * @param {number} v00 The values at (0, 0). + * @param {number} v10 The values at (1, 0). + * @param {number} v20 The values at (2, 0). + * @param {number} v30 The values at (3, 0). + * @param {number} v01 The values at (0, 1). + * @param {number} v11 The values at (1, 1). + * @param {number} v21 The values at (2, 1). + * @param {number} v31 The values at (3, 1). + * @param {number} v02 The values at (0, 2). + * @param {number} v12 The values at (1, 2). + * @param {number} v22 The values at (2, 2). + * @param {number} v32 The values at (3, 2). + * @param {number} v03 The values at (0, 3). + * @param {number} v13 The values at (1, 3). + * @param {number} v23 The values at (2, 3). + * @param {number} v33 The values at (3, 3). + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setFromValues = function( + mat, v00, v10, v20, v30, v01, v11, v21, v31, v02, v12, v22, v32, v03, v13, + v23, v33) { + mat[0] = v00; + mat[1] = v10; + mat[2] = v20; + mat[3] = v30; + mat[4] = v01; + mat[5] = v11; + mat[6] = v21; + mat[7] = v31; + mat[8] = v02; + mat[9] = v12; + mat[10] = v22; + mat[11] = v32; + mat[12] = v03; + mat[13] = v13; + mat[14] = v23; + mat[15] = v33; + return mat; +}; + + +/** + * Sets the matrix from the array of values stored in column major order. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {goog.vec.Mat4.AnyType} values The column major ordered + * array of values to store in the matrix. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setFromArray = function(mat, values) { + mat[0] = values[0]; + mat[1] = values[1]; + mat[2] = values[2]; + mat[3] = values[3]; + mat[4] = values[4]; + mat[5] = values[5]; + mat[6] = values[6]; + mat[7] = values[7]; + mat[8] = values[8]; + mat[9] = values[9]; + mat[10] = values[10]; + mat[11] = values[11]; + mat[12] = values[12]; + mat[13] = values[13]; + mat[14] = values[14]; + mat[15] = values[15]; + return mat; +}; + + +/** + * Sets the matrix from the array of values stored in row major order. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {goog.vec.Mat4.AnyType} values The row major ordered array of + * values to store in the matrix. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setFromRowMajorArray = function(mat, values) { + mat[0] = values[0]; + mat[1] = values[4]; + mat[2] = values[8]; + mat[3] = values[12]; + + mat[4] = values[1]; + mat[5] = values[5]; + mat[6] = values[9]; + mat[7] = values[13]; + + mat[8] = values[2]; + mat[9] = values[6]; + mat[10] = values[10]; + mat[11] = values[14]; + + mat[12] = values[3]; + mat[13] = values[7]; + mat[14] = values[11]; + mat[15] = values[15]; + + return mat; +}; + + +/** + * Sets the diagonal values of the matrix from the given values. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {number} v00 The values for (0, 0). + * @param {number} v11 The values for (1, 1). + * @param {number} v22 The values for (2, 2). + * @param {number} v33 The values for (3, 3). + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setDiagonalValues = function(mat, v00, v11, v22, v33) { + mat[0] = v00; + mat[5] = v11; + mat[10] = v22; + mat[15] = v33; + return mat; +}; + + +/** + * Sets the diagonal values of the matrix from the given vector. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {goog.vec.Vec4.AnyType} vec The vector containing the values. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setDiagonal = function(mat, vec) { + mat[0] = vec[0]; + mat[5] = vec[1]; + mat[10] = vec[2]; + mat[15] = vec[3]; + return mat; +}; + + +/** + * Gets the diagonal values of the matrix into the given vector. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix containing the values. + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the values. + * @param {number=} opt_diagonal Which diagonal to get. A value of 0 selects the + * main diagonal, a positive number selects a super diagonal and a negative + * number selects a sub diagonal. + * @return {goog.vec.Vec4.AnyType} return vec so that operations can be + * chained together. + */ +goog.vec.Mat4.getDiagonal = function(mat, vec, opt_diagonal) { + if (!opt_diagonal) { + // This is the most common case, so we avoid the for loop. + vec[0] = mat[0]; + vec[1] = mat[5]; + vec[2] = mat[10]; + vec[3] = mat[15]; + } else { + var offset = opt_diagonal > 0 ? 4 * opt_diagonal : -opt_diagonal; + for (var i = 0; i < 4 - Math.abs(opt_diagonal); i++) { + vec[i] = mat[offset + 5 * i]; + } + } + return vec; +}; + + +/** + * Sets the specified column with the supplied values. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {number} column The column index to set the values on. + * @param {number} v0 The value for row 0. + * @param {number} v1 The value for row 1. + * @param {number} v2 The value for row 2. + * @param {number} v3 The value for row 3. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setColumnValues = function(mat, column, v0, v1, v2, v3) { + var i = column * 4; + mat[i] = v0; + mat[i + 1] = v1; + mat[i + 2] = v2; + mat[i + 3] = v3; + return mat; +}; + + +/** + * Sets the specified column with the value from the supplied vector. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {number} column The column index to set the values on. + * @param {goog.vec.Vec4.AnyType} vec The vector of elements for the column. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setColumn = function(mat, column, vec) { + var i = column * 4; + mat[i] = vec[0]; + mat[i + 1] = vec[1]; + mat[i + 2] = vec[2]; + mat[i + 3] = vec[3]; + return mat; +}; + + +/** + * Retrieves the specified column from the matrix into the given vector. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the values. + * @param {number} column The column to get the values from. + * @param {goog.vec.Vec4.AnyType} vec The vector of elements to + * receive the column. + * @return {goog.vec.Vec4.AnyType} return vec so that operations can be + * chained together. + */ +goog.vec.Mat4.getColumn = function(mat, column, vec) { + var i = column * 4; + vec[0] = mat[i]; + vec[1] = mat[i + 1]; + vec[2] = mat[i + 2]; + vec[3] = mat[i + 3]; + return vec; +}; + + +/** + * Sets the columns of the matrix from the given vectors. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {goog.vec.Vec4.AnyType} vec0 The values for column 0. + * @param {goog.vec.Vec4.AnyType} vec1 The values for column 1. + * @param {goog.vec.Vec4.AnyType} vec2 The values for column 2. + * @param {goog.vec.Vec4.AnyType} vec3 The values for column 3. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setColumns = function(mat, vec0, vec1, vec2, vec3) { + goog.vec.Mat4.setColumn(mat, 0, vec0); + goog.vec.Mat4.setColumn(mat, 1, vec1); + goog.vec.Mat4.setColumn(mat, 2, vec2); + goog.vec.Mat4.setColumn(mat, 3, vec3); + return mat; +}; + + +/** + * Retrieves the column values from the given matrix into the given vectors. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the columns. + * @param {goog.vec.Vec4.AnyType} vec0 The vector to receive column 0. + * @param {goog.vec.Vec4.AnyType} vec1 The vector to receive column 1. + * @param {goog.vec.Vec4.AnyType} vec2 The vector to receive column 2. + * @param {goog.vec.Vec4.AnyType} vec3 The vector to receive column 3. + */ +goog.vec.Mat4.getColumns = function(mat, vec0, vec1, vec2, vec3) { + goog.vec.Mat4.getColumn(mat, 0, vec0); + goog.vec.Mat4.getColumn(mat, 1, vec1); + goog.vec.Mat4.getColumn(mat, 2, vec2); + goog.vec.Mat4.getColumn(mat, 3, vec3); +}; + + +/** + * Sets the row values from the supplied values. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {number} row The index of the row to receive the values. + * @param {number} v0 The value for column 0. + * @param {number} v1 The value for column 1. + * @param {number} v2 The value for column 2. + * @param {number} v3 The value for column 3. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setRowValues = function(mat, row, v0, v1, v2, v3) { + mat[row] = v0; + mat[row + 4] = v1; + mat[row + 8] = v2; + mat[row + 12] = v3; + return mat; +}; + + +/** + * Sets the row values from the supplied vector. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the row values. + * @param {number} row The index of the row. + * @param {goog.vec.Vec4.AnyType} vec The vector containing the values. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setRow = function(mat, row, vec) { + mat[row] = vec[0]; + mat[row + 4] = vec[1]; + mat[row + 8] = vec[2]; + mat[row + 12] = vec[3]; + return mat; +}; + + +/** + * Retrieves the row values into the given vector. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the values. + * @param {number} row The index of the row supplying the values. + * @param {goog.vec.Vec4.AnyType} vec The vector to receive the row. + * @return {goog.vec.Vec4.AnyType} return vec so that operations can be + * chained together. + */ +goog.vec.Mat4.getRow = function(mat, row, vec) { + vec[0] = mat[row]; + vec[1] = mat[row + 4]; + vec[2] = mat[row + 8]; + vec[3] = mat[row + 12]; + return vec; +}; + + +/** + * Sets the rows of the matrix from the supplied vectors. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to receive the values. + * @param {goog.vec.Vec4.AnyType} vec0 The values for row 0. + * @param {goog.vec.Vec4.AnyType} vec1 The values for row 1. + * @param {goog.vec.Vec4.AnyType} vec2 The values for row 2. + * @param {goog.vec.Vec4.AnyType} vec3 The values for row 3. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained together. + */ +goog.vec.Mat4.setRows = function(mat, vec0, vec1, vec2, vec3) { + goog.vec.Mat4.setRow(mat, 0, vec0); + goog.vec.Mat4.setRow(mat, 1, vec1); + goog.vec.Mat4.setRow(mat, 2, vec2); + goog.vec.Mat4.setRow(mat, 3, vec3); + return mat; +}; + + +/** + * Retrieves the rows of the matrix into the supplied vectors. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to supply the values. + * @param {goog.vec.Vec4.AnyType} vec0 The vector to receive row 0. + * @param {goog.vec.Vec4.AnyType} vec1 The vector to receive row 1. + * @param {goog.vec.Vec4.AnyType} vec2 The vector to receive row 2. + * @param {goog.vec.Vec4.AnyType} vec3 The vector to receive row 3. + */ +goog.vec.Mat4.getRows = function(mat, vec0, vec1, vec2, vec3) { + goog.vec.Mat4.getRow(mat, 0, vec0); + goog.vec.Mat4.getRow(mat, 1, vec1); + goog.vec.Mat4.getRow(mat, 2, vec2); + goog.vec.Mat4.getRow(mat, 3, vec3); +}; + + +/** + * Makes the given 4x4 matrix the zero matrix. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @return {!goog.vec.Mat4.AnyType} return mat so operations can be chained. + */ +goog.vec.Mat4.makeZero = function(mat) { + mat[0] = 0; + mat[1] = 0; + mat[2] = 0; + mat[3] = 0; + mat[4] = 0; + mat[5] = 0; + mat[6] = 0; + mat[7] = 0; + mat[8] = 0; + mat[9] = 0; + mat[10] = 0; + mat[11] = 0; + mat[12] = 0; + mat[13] = 0; + mat[14] = 0; + mat[15] = 0; + return mat; +}; + + +/** + * Makes the given 4x4 matrix the identity matrix. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @return {goog.vec.Mat4.AnyType} return mat so operations can be chained. + */ +goog.vec.Mat4.makeIdentity = function(mat) { + mat[0] = 1; + mat[1] = 0; + mat[2] = 0; + mat[3] = 0; + mat[4] = 0; + mat[5] = 1; + mat[6] = 0; + mat[7] = 0; + mat[8] = 0; + mat[9] = 0; + mat[10] = 1; + mat[11] = 0; + mat[12] = 0; + mat[13] = 0; + mat[14] = 0; + mat[15] = 1; + return mat; +}; + + +/** + * Performs a per-component addition of the matrix mat0 and mat1, storing + * the result into resultMat. + * + * @param {goog.vec.Mat4.AnyType} mat0 The first addend. + * @param {goog.vec.Mat4.AnyType} mat1 The second addend. + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to + * receive the results (may be either mat0 or mat1). + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be + * chained together. + */ +goog.vec.Mat4.addMat = function(mat0, mat1, resultMat) { + resultMat[0] = mat0[0] + mat1[0]; + resultMat[1] = mat0[1] + mat1[1]; + resultMat[2] = mat0[2] + mat1[2]; + resultMat[3] = mat0[3] + mat1[3]; + resultMat[4] = mat0[4] + mat1[4]; + resultMat[5] = mat0[5] + mat1[5]; + resultMat[6] = mat0[6] + mat1[6]; + resultMat[7] = mat0[7] + mat1[7]; + resultMat[8] = mat0[8] + mat1[8]; + resultMat[9] = mat0[9] + mat1[9]; + resultMat[10] = mat0[10] + mat1[10]; + resultMat[11] = mat0[11] + mat1[11]; + resultMat[12] = mat0[12] + mat1[12]; + resultMat[13] = mat0[13] + mat1[13]; + resultMat[14] = mat0[14] + mat1[14]; + resultMat[15] = mat0[15] + mat1[15]; + return resultMat; +}; + + +/** + * Performs a per-component subtraction of the matrix mat0 and mat1, + * storing the result into resultMat. + * + * @param {goog.vec.Mat4.AnyType} mat0 The minuend. + * @param {goog.vec.Mat4.AnyType} mat1 The subtrahend. + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive + * the results (may be either mat0 or mat1). + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be + * chained together. + */ +goog.vec.Mat4.subMat = function(mat0, mat1, resultMat) { + resultMat[0] = mat0[0] - mat1[0]; + resultMat[1] = mat0[1] - mat1[1]; + resultMat[2] = mat0[2] - mat1[2]; + resultMat[3] = mat0[3] - mat1[3]; + resultMat[4] = mat0[4] - mat1[4]; + resultMat[5] = mat0[5] - mat1[5]; + resultMat[6] = mat0[6] - mat1[6]; + resultMat[7] = mat0[7] - mat1[7]; + resultMat[8] = mat0[8] - mat1[8]; + resultMat[9] = mat0[9] - mat1[9]; + resultMat[10] = mat0[10] - mat1[10]; + resultMat[11] = mat0[11] - mat1[11]; + resultMat[12] = mat0[12] - mat1[12]; + resultMat[13] = mat0[13] - mat1[13]; + resultMat[14] = mat0[14] - mat1[14]; + resultMat[15] = mat0[15] - mat1[15]; + return resultMat; +}; + + +/** + * Multiplies matrix mat with the given scalar, storing the result + * into resultMat. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} scalar The scalar value to multiply to each element of mat. + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive + * the results (may be mat). + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be + * chained together. + */ +goog.vec.Mat4.multScalar = function(mat, scalar, resultMat) { + resultMat[0] = mat[0] * scalar; + resultMat[1] = mat[1] * scalar; + resultMat[2] = mat[2] * scalar; + resultMat[3] = mat[3] * scalar; + resultMat[4] = mat[4] * scalar; + resultMat[5] = mat[5] * scalar; + resultMat[6] = mat[6] * scalar; + resultMat[7] = mat[7] * scalar; + resultMat[8] = mat[8] * scalar; + resultMat[9] = mat[9] * scalar; + resultMat[10] = mat[10] * scalar; + resultMat[11] = mat[11] * scalar; + resultMat[12] = mat[12] * scalar; + resultMat[13] = mat[13] * scalar; + resultMat[14] = mat[14] * scalar; + resultMat[15] = mat[15] * scalar; + return resultMat; +}; + + +/** + * Multiplies the two matrices mat0 and mat1 using matrix multiplication, + * storing the result into resultMat. + * + * @param {goog.vec.Mat4.AnyType} mat0 The first (left hand) matrix. + * @param {goog.vec.Mat4.AnyType} mat1 The second (right hand) matrix. + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive + * the results (may be either mat0 or mat1). + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be + * chained together. + */ +goog.vec.Mat4.multMat = function(mat0, mat1, resultMat) { + var a00 = mat0[0], a10 = mat0[1], a20 = mat0[2], a30 = mat0[3]; + var a01 = mat0[4], a11 = mat0[5], a21 = mat0[6], a31 = mat0[7]; + var a02 = mat0[8], a12 = mat0[9], a22 = mat0[10], a32 = mat0[11]; + var a03 = mat0[12], a13 = mat0[13], a23 = mat0[14], a33 = mat0[15]; + + var b00 = mat1[0], b10 = mat1[1], b20 = mat1[2], b30 = mat1[3]; + var b01 = mat1[4], b11 = mat1[5], b21 = mat1[6], b31 = mat1[7]; + var b02 = mat1[8], b12 = mat1[9], b22 = mat1[10], b32 = mat1[11]; + var b03 = mat1[12], b13 = mat1[13], b23 = mat1[14], b33 = mat1[15]; + + resultMat[0] = a00 * b00 + a01 * b10 + a02 * b20 + a03 * b30; + resultMat[1] = a10 * b00 + a11 * b10 + a12 * b20 + a13 * b30; + resultMat[2] = a20 * b00 + a21 * b10 + a22 * b20 + a23 * b30; + resultMat[3] = a30 * b00 + a31 * b10 + a32 * b20 + a33 * b30; + + resultMat[4] = a00 * b01 + a01 * b11 + a02 * b21 + a03 * b31; + resultMat[5] = a10 * b01 + a11 * b11 + a12 * b21 + a13 * b31; + resultMat[6] = a20 * b01 + a21 * b11 + a22 * b21 + a23 * b31; + resultMat[7] = a30 * b01 + a31 * b11 + a32 * b21 + a33 * b31; + + resultMat[8] = a00 * b02 + a01 * b12 + a02 * b22 + a03 * b32; + resultMat[9] = a10 * b02 + a11 * b12 + a12 * b22 + a13 * b32; + resultMat[10] = a20 * b02 + a21 * b12 + a22 * b22 + a23 * b32; + resultMat[11] = a30 * b02 + a31 * b12 + a32 * b22 + a33 * b32; + + resultMat[12] = a00 * b03 + a01 * b13 + a02 * b23 + a03 * b33; + resultMat[13] = a10 * b03 + a11 * b13 + a12 * b23 + a13 * b33; + resultMat[14] = a20 * b03 + a21 * b13 + a22 * b23 + a23 * b33; + resultMat[15] = a30 * b03 + a31 * b13 + a32 * b23 + a33 * b33; + return resultMat; +}; + + +/** + * Transposes the given matrix mat storing the result into resultMat. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to transpose. + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive + * the results (may be mat). + * @return {goog.vec.Mat4.AnyType} return resultMat so that operations can be + * chained together. + */ +goog.vec.Mat4.transpose = function(mat, resultMat) { + if (resultMat == mat) { + var a10 = mat[1], a20 = mat[2], a30 = mat[3]; + var a21 = mat[6], a31 = mat[7]; + var a32 = mat[11]; + resultMat[1] = mat[4]; + resultMat[2] = mat[8]; + resultMat[3] = mat[12]; + resultMat[4] = a10; + resultMat[6] = mat[9]; + resultMat[7] = mat[13]; + resultMat[8] = a20; + resultMat[9] = a21; + resultMat[11] = mat[14]; + resultMat[12] = a30; + resultMat[13] = a31; + resultMat[14] = a32; + } else { + resultMat[0] = mat[0]; + resultMat[1] = mat[4]; + resultMat[2] = mat[8]; + resultMat[3] = mat[12]; + + resultMat[4] = mat[1]; + resultMat[5] = mat[5]; + resultMat[6] = mat[9]; + resultMat[7] = mat[13]; + + resultMat[8] = mat[2]; + resultMat[9] = mat[6]; + resultMat[10] = mat[10]; + resultMat[11] = mat[14]; + + resultMat[12] = mat[3]; + resultMat[13] = mat[7]; + resultMat[14] = mat[11]; + resultMat[15] = mat[15]; + } + return resultMat; +}; + + +/** + * Computes the determinant of the matrix. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to compute the matrix for. + * @return {number} The determinant of the matrix. + */ +goog.vec.Mat4.determinant = function(mat) { + var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3]; + var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7]; + var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11]; + var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15]; + + var a0 = m00 * m11 - m10 * m01; + var a1 = m00 * m21 - m20 * m01; + var a2 = m00 * m31 - m30 * m01; + var a3 = m10 * m21 - m20 * m11; + var a4 = m10 * m31 - m30 * m11; + var a5 = m20 * m31 - m30 * m21; + var b0 = m02 * m13 - m12 * m03; + var b1 = m02 * m23 - m22 * m03; + var b2 = m02 * m33 - m32 * m03; + var b3 = m12 * m23 - m22 * m13; + var b4 = m12 * m33 - m32 * m13; + var b5 = m22 * m33 - m32 * m23; + + return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; +}; + + +/** + * Computes the inverse of mat storing the result into resultMat. If the + * inverse is defined, this function returns true, false otherwise. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix to invert. + * @param {goog.vec.Mat4.AnyType} resultMat The matrix to receive + * the result (may be mat). + * @return {boolean} True if the inverse is defined. If false is returned, + * resultMat is not modified. + */ +goog.vec.Mat4.invert = function(mat, resultMat) { + var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3]; + var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7]; + var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11]; + var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15]; + + var a0 = m00 * m11 - m10 * m01; + var a1 = m00 * m21 - m20 * m01; + var a2 = m00 * m31 - m30 * m01; + var a3 = m10 * m21 - m20 * m11; + var a4 = m10 * m31 - m30 * m11; + var a5 = m20 * m31 - m30 * m21; + var b0 = m02 * m13 - m12 * m03; + var b1 = m02 * m23 - m22 * m03; + var b2 = m02 * m33 - m32 * m03; + var b3 = m12 * m23 - m22 * m13; + var b4 = m12 * m33 - m32 * m13; + var b5 = m22 * m33 - m32 * m23; + + var det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0; + if (det == 0) { + return false; + } + + var idet = 1.0 / det; + resultMat[0] = (m11 * b5 - m21 * b4 + m31 * b3) * idet; + resultMat[1] = (-m10 * b5 + m20 * b4 - m30 * b3) * idet; + resultMat[2] = (m13 * a5 - m23 * a4 + m33 * a3) * idet; + resultMat[3] = (-m12 * a5 + m22 * a4 - m32 * a3) * idet; + resultMat[4] = (-m01 * b5 + m21 * b2 - m31 * b1) * idet; + resultMat[5] = (m00 * b5 - m20 * b2 + m30 * b1) * idet; + resultMat[6] = (-m03 * a5 + m23 * a2 - m33 * a1) * idet; + resultMat[7] = (m02 * a5 - m22 * a2 + m32 * a1) * idet; + resultMat[8] = (m01 * b4 - m11 * b2 + m31 * b0) * idet; + resultMat[9] = (-m00 * b4 + m10 * b2 - m30 * b0) * idet; + resultMat[10] = (m03 * a4 - m13 * a2 + m33 * a0) * idet; + resultMat[11] = (-m02 * a4 + m12 * a2 - m32 * a0) * idet; + resultMat[12] = (-m01 * b3 + m11 * b1 - m21 * b0) * idet; + resultMat[13] = (m00 * b3 - m10 * b1 + m20 * b0) * idet; + resultMat[14] = (-m03 * a3 + m13 * a1 - m23 * a0) * idet; + resultMat[15] = (m02 * a3 - m12 * a1 + m22 * a0) * idet; + return true; +}; + + +/** + * Returns true if the components of mat0 are equal to the components of mat1. + * + * @param {goog.vec.Mat4.AnyType} mat0 The first matrix. + * @param {goog.vec.Mat4.AnyType} mat1 The second matrix. + * @return {boolean} True if the the two matrices are equivalent. + */ +goog.vec.Mat4.equals = function(mat0, mat1) { + return mat0.length == mat1.length && mat0[0] == mat1[0] && + mat0[1] == mat1[1] && mat0[2] == mat1[2] && mat0[3] == mat1[3] && + mat0[4] == mat1[4] && mat0[5] == mat1[5] && mat0[6] == mat1[6] && + mat0[7] == mat1[7] && mat0[8] == mat1[8] && mat0[9] == mat1[9] && + mat0[10] == mat1[10] && mat0[11] == mat1[11] && mat0[12] == mat1[12] && + mat0[13] == mat1[13] && mat0[14] == mat1[14] && mat0[15] == mat1[15]; +}; + + +/** + * Transforms the given vector with the given matrix storing the resulting, + * transformed vector into resultVec. The input vector is multiplied against the + * upper 3x4 matrix omitting the projective component. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation. + * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform. + * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector to + * receive the results (may be vec). + * @return {goog.vec.Vec3.AnyType} return resultVec so that operations can be + * chained together. + */ +goog.vec.Mat4.multVec3 = function(mat, vec, resultVec) { + var x = vec[0], y = vec[1], z = vec[2]; + resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + mat[12]; + resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + mat[13]; + resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + mat[14]; + return resultVec; +}; + + +/** + * Transforms the given vector with the given matrix storing the resulting, + * transformed vector into resultVec. The input vector is multiplied against the + * upper 3x3 matrix omitting the projective component and translation + * components. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation. + * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform. + * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector to + * receive the results (may be vec). + * @return {goog.vec.Vec3.AnyType} return resultVec so that operations can be + * chained together. + */ +goog.vec.Mat4.multVec3NoTranslate = function(mat, vec, resultVec) { + var x = vec[0], y = vec[1], z = vec[2]; + resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8]; + resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9]; + resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10]; + return resultVec; +}; + + +/** + * Transforms the given vector with the given matrix storing the resulting, + * transformed vector into resultVec. The input vector is multiplied against the + * full 4x4 matrix with the homogeneous divide applied to reduce the 4 element + * vector to a 3 element vector. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation. + * @param {goog.vec.Vec3.AnyType} vec The 3 element vector to transform. + * @param {goog.vec.Vec3.AnyType} resultVec The 3 element vector + * to receive the results (may be vec). + * @return {goog.vec.Vec3.AnyType} return resultVec so that operations can be + * chained together. + */ +goog.vec.Mat4.multVec3Projective = function(mat, vec, resultVec) { + var x = vec[0], y = vec[1], z = vec[2]; + var invw = 1 / (x * mat[3] + y * mat[7] + z * mat[11] + mat[15]); + resultVec[0] = (x * mat[0] + y * mat[4] + z * mat[8] + mat[12]) * invw; + resultVec[1] = (x * mat[1] + y * mat[5] + z * mat[9] + mat[13]) * invw; + resultVec[2] = (x * mat[2] + y * mat[6] + z * mat[10] + mat[14]) * invw; + return resultVec; +}; + + +/** + * Transforms the given vector with the given matrix storing the resulting, + * transformed vector into resultVec. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix supplying the transformation. + * @param {goog.vec.Vec4.AnyType} vec The vector to transform. + * @param {goog.vec.Vec4.AnyType} resultVec The vector to + * receive the results (may be vec). + * @return {goog.vec.Vec4.AnyType} return resultVec so that operations can be + * chained together. + */ +goog.vec.Mat4.multVec4 = function(mat, vec, resultVec) { + var x = vec[0], y = vec[1], z = vec[2], w = vec[3]; + resultVec[0] = x * mat[0] + y * mat[4] + z * mat[8] + w * mat[12]; + resultVec[1] = x * mat[1] + y * mat[5] + z * mat[9] + w * mat[13]; + resultVec[2] = x * mat[2] + y * mat[6] + z * mat[10] + w * mat[14]; + resultVec[3] = x * mat[3] + y * mat[7] + z * mat[11] + w * mat[15]; + return resultVec; +}; + + +/** + * Makes the given 4x4 matrix a translation matrix with x, y and z + * translation factors. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} x The translation along the x axis. + * @param {number} y The translation along the y axis. + * @param {number} z The translation along the z axis. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeTranslate = function(mat, x, y, z) { + goog.vec.Mat4.makeIdentity(mat); + return goog.vec.Mat4.setColumnValues(mat, 3, x, y, z, 1); +}; + + +/** + * Makes the given 4x4 matrix as a scale matrix with x, y and z scale factors. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} x The scale along the x axis. + * @param {number} y The scale along the y axis. + * @param {number} z The scale along the z axis. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeScale = function(mat, x, y, z) { + goog.vec.Mat4.makeIdentity(mat); + return goog.vec.Mat4.setDiagonalValues(mat, x, y, z, 1); +}; + + +/** + * Makes the given 4x4 matrix a rotation matrix with the given rotation + * angle about the axis defined by the vector (ax, ay, az). + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The rotation angle in radians. + * @param {number} ax The x component of the rotation axis. + * @param {number} ay The y component of the rotation axis. + * @param {number} az The z component of the rotation axis. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeRotate = function(mat, angle, ax, ay, az) { + var c = Math.cos(angle); + var d = 1 - c; + var s = Math.sin(angle); + + return goog.vec.Mat4.setFromValues( + mat, ax * ax * d + c, ax * ay * d + az * s, ax * az * d - ay * s, 0, + + ax * ay * d - az * s, ay * ay * d + c, ay * az * d + ax * s, 0, + + ax * az * d + ay * s, ay * az * d - ax * s, az * az * d + c, 0, + + 0, 0, 0, 1); +}; + + +/** + * Makes the given 4x4 matrix a rotation matrix with the given rotation + * angle about the X axis. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The rotation angle in radians. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeRotateX = function(mat, angle) { + var c = Math.cos(angle); + var s = Math.sin(angle); + return goog.vec.Mat4.setFromValues( + mat, 1, 0, 0, 0, 0, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1); +}; + + +/** + * Makes the given 4x4 matrix a rotation matrix with the given rotation + * angle about the Y axis. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The rotation angle in radians. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeRotateY = function(mat, angle) { + var c = Math.cos(angle); + var s = Math.sin(angle); + return goog.vec.Mat4.setFromValues( + mat, c, 0, -s, 0, 0, 1, 0, 0, s, 0, c, 0, 0, 0, 0, 1); +}; + + +/** + * Makes the given 4x4 matrix a rotation matrix with the given rotation + * angle about the Z axis. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The rotation angle in radians. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeRotateZ = function(mat, angle) { + var c = Math.cos(angle); + var s = Math.sin(angle); + return goog.vec.Mat4.setFromValues( + mat, c, s, 0, 0, -s, c, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); +}; + + +/** + * Makes the given 4x4 matrix a perspective projection matrix. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} left The coordinate of the left clipping plane. + * @param {number} right The coordinate of the right clipping plane. + * @param {number} bottom The coordinate of the bottom clipping plane. + * @param {number} top The coordinate of the top clipping plane. + * @param {number} near The distance to the near clipping plane. + * @param {number} far The distance to the far clipping plane. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeFrustum = function(mat, left, right, bottom, top, near, far) { + var x = (2 * near) / (right - left); + var y = (2 * near) / (top - bottom); + var a = (right + left) / (right - left); + var b = (top + bottom) / (top - bottom); + var c = -(far + near) / (far - near); + var d = -(2 * far * near) / (far - near); + + return goog.vec.Mat4.setFromValues( + mat, x, 0, 0, 0, 0, y, 0, 0, a, b, c, -1, 0, 0, d, 0); +}; + + +/** + * Makes the given 4x4 matrix perspective projection matrix given a + * field of view and aspect ratio. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} fovy The field of view along the y (vertical) axis in + * radians. + * @param {number} aspect The x (width) to y (height) aspect ratio. + * @param {number} near The distance to the near clipping plane. + * @param {number} far The distance to the far clipping plane. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makePerspective = function(mat, fovy, aspect, near, far) { + var angle = fovy / 2; + var dz = far - near; + var sinAngle = Math.sin(angle); + if (dz == 0 || sinAngle == 0 || aspect == 0) { + return mat; + } + + var cot = Math.cos(angle) / sinAngle; + return goog.vec.Mat4.setFromValues( + mat, cot / aspect, 0, 0, 0, 0, cot, 0, 0, 0, 0, -(far + near) / dz, -1, 0, + 0, -(2 * near * far) / dz, 0); +}; + + +/** + * Makes the given 4x4 matrix an orthographic projection matrix. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} left The coordinate of the left clipping plane. + * @param {number} right The coordinate of the right clipping plane. + * @param {number} bottom The coordinate of the bottom clipping plane. + * @param {number} top The coordinate of the top clipping plane. + * @param {number} near The distance to the near clipping plane. + * @param {number} far The distance to the far clipping plane. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeOrtho = function(mat, left, right, bottom, top, near, far) { + var x = 2 / (right - left); + var y = 2 / (top - bottom); + var z = -2 / (far - near); + var a = -(right + left) / (right - left); + var b = -(top + bottom) / (top - bottom); + var c = -(far + near) / (far - near); + + return goog.vec.Mat4.setFromValues( + mat, x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, a, b, c, 1); +}; + + +/** + * Makes the given 4x4 matrix a modelview matrix of a camera so that + * the camera is 'looking at' the given center point. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {goog.vec.Vec3.AnyType} eyePt The position of the eye point + * (camera origin). + * @param {goog.vec.Vec3.AnyType} centerPt The point to aim the camera at. + * @param {goog.vec.Vec3.AnyType} worldUpVec The vector that identifies + * the up direction for the camera. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeLookAt = function(mat, eyePt, centerPt, worldUpVec) { + // Compute the direction vector from the eye point to the center point and + // normalize. + var fwdVec = goog.vec.Mat4.tmpVec4_[0]; + goog.vec.Vec3.subtract(centerPt, eyePt, fwdVec); + goog.vec.Vec3.normalize(fwdVec, fwdVec); + fwdVec[3] = 0; + + // Compute the side vector from the forward vector and the input up vector. + var sideVec = goog.vec.Mat4.tmpVec4_[1]; + goog.vec.Vec3.cross(fwdVec, worldUpVec, sideVec); + goog.vec.Vec3.normalize(sideVec, sideVec); + sideVec[3] = 0; + + // Now the up vector to form the orthonormal basis. + var upVec = goog.vec.Mat4.tmpVec4_[2]; + goog.vec.Vec3.cross(sideVec, fwdVec, upVec); + goog.vec.Vec3.normalize(upVec, upVec); + upVec[3] = 0; + + // Update the view matrix with the new orthonormal basis and position the + // camera at the given eye point. + goog.vec.Vec3.negate(fwdVec, fwdVec); + goog.vec.Mat4.setRow(mat, 0, sideVec); + goog.vec.Mat4.setRow(mat, 1, upVec); + goog.vec.Mat4.setRow(mat, 2, fwdVec); + goog.vec.Mat4.setRowValues(mat, 3, 0, 0, 0, 1); + goog.vec.Mat4.translate(mat, -eyePt[0], -eyePt[1], -eyePt[2]); + + return mat; +}; + + +/** + * Decomposes a matrix into the lookAt vectors eyePt, fwdVec and worldUpVec. + * The matrix represents the modelview matrix of a camera. It is the inverse + * of lookAt except for the output of the fwdVec instead of centerPt. + * The centerPt itself cannot be recovered from a modelview matrix. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {goog.vec.Vec3.AnyType} eyePt The position of the eye point + * (camera origin). + * @param {goog.vec.Vec3.AnyType} fwdVec The vector describing where + * the camera points to. + * @param {goog.vec.Vec3.AnyType} worldUpVec The vector that + * identifies the up direction for the camera. + * @return {boolean} True if the method succeeds, false otherwise. + * The method can only fail if the inverse of viewMatrix is not defined. + */ +goog.vec.Mat4.toLookAt = function(mat, eyePt, fwdVec, worldUpVec) { + // Get eye of the camera. + var matInverse = goog.vec.Mat4.tmpMat4_[0]; + if (!goog.vec.Mat4.invert(mat, matInverse)) { + // The input matrix does not have a valid inverse. + return false; + } + + if (eyePt) { + eyePt[0] = matInverse[12]; + eyePt[1] = matInverse[13]; + eyePt[2] = matInverse[14]; + } + + // Get forward vector from the definition of lookAt. + if (fwdVec || worldUpVec) { + if (!fwdVec) { + fwdVec = goog.vec.Mat4.tmpVec3_[0]; + } + fwdVec[0] = -mat[2]; + fwdVec[1] = -mat[6]; + fwdVec[2] = -mat[10]; + // Normalize forward vector. + goog.vec.Vec3.normalize(fwdVec, fwdVec); + } + + if (worldUpVec) { + // Get side vector from the definition of gluLookAt. + var side = goog.vec.Mat4.tmpVec3_[1]; + side[0] = mat[0]; + side[1] = mat[4]; + side[2] = mat[8]; + // Compute up vector as a up = side x forward. + goog.vec.Vec3.cross(side, fwdVec, worldUpVec); + // Normalize up vector. + goog.vec.Vec3.normalize(worldUpVec, worldUpVec); + } + return true; +}; + + +/** + * Makes the given 4x4 matrix a rotation matrix given Euler angles using + * the ZXZ convention. + * Given the euler angles [theta1, theta2, theta3], the rotation is defined as + * rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3), + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi]. + * rotation_x(theta) means rotation around the X axis of theta radians, + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} theta1 The angle of rotation around the Z axis in radians. + * @param {number} theta2 The angle of rotation around the X axis in radians. + * @param {number} theta3 The angle of rotation around the Z axis in radians. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.makeEulerZXZ = function(mat, theta1, theta2, theta3) { + var c1 = Math.cos(theta1); + var s1 = Math.sin(theta1); + + var c2 = Math.cos(theta2); + var s2 = Math.sin(theta2); + + var c3 = Math.cos(theta3); + var s3 = Math.sin(theta3); + + mat[0] = c1 * c3 - c2 * s1 * s3; + mat[1] = c2 * c1 * s3 + c3 * s1; + mat[2] = s3 * s2; + mat[3] = 0; + + mat[4] = -c1 * s3 - c3 * c2 * s1; + mat[5] = c1 * c2 * c3 - s1 * s3; + mat[6] = c3 * s2; + mat[7] = 0; + + mat[8] = s2 * s1; + mat[9] = -c1 * s2; + mat[10] = c2; + mat[11] = 0; + + mat[12] = 0; + mat[13] = 0; + mat[14] = 0; + mat[15] = 1; + + return mat; +}; + + +/** + * Decomposes a rotation matrix into Euler angles using the ZXZ convention so + * that rotation = rotation_z(theta1) * rotation_x(theta2) * rotation_z(theta3), + * with theta1 in [0, 2 * pi], theta2 in [0, pi] and theta3 in [0, 2 * pi]. + * rotation_x(theta) means rotation around the X axis of theta radians. + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {goog.vec.Vec3.AnyType} euler The ZXZ Euler angles in + * radians as [theta1, theta2, theta3]. + * @param {boolean=} opt_theta2IsNegative Whether theta2 is in [-pi, 0] instead + * of the default [0, pi]. + * @return {goog.vec.Vec4.AnyType} return euler so that operations can be + * chained together. + */ +goog.vec.Mat4.toEulerZXZ = function(mat, euler, opt_theta2IsNegative) { + // There is an ambiguity in the sign of sinTheta2 because of the sqrt. + var sinTheta2 = Math.sqrt(mat[2] * mat[2] + mat[6] * mat[6]); + + // By default we explicitely constrain theta2 to be in [0, pi], + // so sinTheta2 is always positive. We can change the behavior and specify + // theta2 to be negative in [-pi, 0] with opt_Theta2IsNegative. + var signTheta2 = opt_theta2IsNegative ? -1 : 1; + + if (sinTheta2 > goog.vec.EPSILON) { + euler[2] = Math.atan2(mat[2] * signTheta2, mat[6] * signTheta2); + euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]); + euler[0] = Math.atan2(mat[8] * signTheta2, -mat[9] * signTheta2); + } else { + // There is also an arbitrary choice for theta1 = 0 or theta2 = 0 here. + // We assume theta1 = 0 as some applications do not allow the camera to roll + // (i.e. have theta1 != 0). + euler[0] = 0; + euler[1] = Math.atan2(sinTheta2 * signTheta2, mat[10]); + euler[2] = Math.atan2(mat[1], mat[0]); + } + + // Atan2 outputs angles in [-pi, pi] so we bring them back to [0, 2 * pi]. + euler[0] = (euler[0] + Math.PI * 2) % (Math.PI * 2); + euler[2] = (euler[2] + Math.PI * 2) % (Math.PI * 2); + // For theta2 we want the angle to be in [0, pi] or [-pi, 0] depending on + // signTheta2. + euler[1] = + ((euler[1] * signTheta2 + Math.PI * 2) % (Math.PI * 2)) * signTheta2; + + return euler; +}; + + +/** + * Translates the given matrix by x,y,z. Equvialent to: + * goog.vec.Mat4.multMat( + * mat, + * goog.vec.Mat4.makeTranslate(goog.vec.Mat4.create(), x, y, z), + * mat); + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} x The translation along the x axis. + * @param {number} y The translation along the y axis. + * @param {number} z The translation along the z axis. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.translate = function(mat, x, y, z) { + return goog.vec.Mat4.setColumnValues( + mat, 3, mat[0] * x + mat[4] * y + mat[8] * z + mat[12], + mat[1] * x + mat[5] * y + mat[9] * z + mat[13], + mat[2] * x + mat[6] * y + mat[10] * z + mat[14], + mat[3] * x + mat[7] * y + mat[11] * z + mat[15]); +}; + + +/** + * Scales the given matrix by x,y,z. Equivalent to: + * goog.vec.Mat4.multMat( + * mat, + * goog.vec.Mat4.makeScale(goog.vec.Mat4.create(), x, y, z), + * mat); + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} x The x scale factor. + * @param {number} y The y scale factor. + * @param {number} z The z scale factor. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.scale = function(mat, x, y, z) { + return goog.vec.Mat4.setFromValues( + mat, mat[0] * x, mat[1] * x, mat[2] * x, mat[3] * x, mat[4] * y, + mat[5] * y, mat[6] * y, mat[7] * y, mat[8] * z, mat[9] * z, mat[10] * z, + mat[11] * z, mat[12], mat[13], mat[14], mat[15]); +}; + + +/** + * Rotate the given matrix by angle about the x,y,z axis. Equivalent to: + * goog.vec.Mat4.multMat( + * mat, + * goog.vec.Mat4.makeRotate(goog.vec.Mat4.create(), angle, x, y, z), + * mat); + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The angle in radians. + * @param {number} x The x component of the rotation axis. + * @param {number} y The y component of the rotation axis. + * @param {number} z The z component of the rotation axis. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.rotate = function(mat, angle, x, y, z) { + var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3]; + var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7]; + var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11]; + var m03 = mat[12], m13 = mat[13], m23 = mat[14], m33 = mat[15]; + + var cosAngle = Math.cos(angle); + var sinAngle = Math.sin(angle); + var diffCosAngle = 1 - cosAngle; + var r00 = x * x * diffCosAngle + cosAngle; + var r10 = x * y * diffCosAngle + z * sinAngle; + var r20 = x * z * diffCosAngle - y * sinAngle; + + var r01 = x * y * diffCosAngle - z * sinAngle; + var r11 = y * y * diffCosAngle + cosAngle; + var r21 = y * z * diffCosAngle + x * sinAngle; + + var r02 = x * z * diffCosAngle + y * sinAngle; + var r12 = y * z * diffCosAngle - x * sinAngle; + var r22 = z * z * diffCosAngle + cosAngle; + + return goog.vec.Mat4.setFromValues( + mat, m00 * r00 + m01 * r10 + m02 * r20, m10 * r00 + m11 * r10 + m12 * r20, + m20 * r00 + m21 * r10 + m22 * r20, m30 * r00 + m31 * r10 + m32 * r20, + + m00 * r01 + m01 * r11 + m02 * r21, m10 * r01 + m11 * r11 + m12 * r21, + m20 * r01 + m21 * r11 + m22 * r21, m30 * r01 + m31 * r11 + m32 * r21, + + m00 * r02 + m01 * r12 + m02 * r22, m10 * r02 + m11 * r12 + m12 * r22, + m20 * r02 + m21 * r12 + m22 * r22, m30 * r02 + m31 * r12 + m32 * r22, + + m03, m13, m23, m33); +}; + + +/** + * Rotate the given matrix by angle about the x axis. Equivalent to: + * goog.vec.Mat4.multMat( + * mat, + * goog.vec.Mat4.makeRotateX(goog.vec.Mat4.create(), angle), + * mat); + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The angle in radians. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.rotateX = function(mat, angle) { + var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7]; + var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11]; + + var c = Math.cos(angle); + var s = Math.sin(angle); + + mat[4] = m01 * c + m02 * s; + mat[5] = m11 * c + m12 * s; + mat[6] = m21 * c + m22 * s; + mat[7] = m31 * c + m32 * s; + mat[8] = m01 * -s + m02 * c; + mat[9] = m11 * -s + m12 * c; + mat[10] = m21 * -s + m22 * c; + mat[11] = m31 * -s + m32 * c; + + return mat; +}; + + +/** + * Rotate the given matrix by angle about the y axis. Equivalent to: + * goog.vec.Mat4.multMat( + * mat, + * goog.vec.Mat4.makeRotateY(goog.vec.Mat4.create(), angle), + * mat); + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The angle in radians. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.rotateY = function(mat, angle) { + var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3]; + var m02 = mat[8], m12 = mat[9], m22 = mat[10], m32 = mat[11]; + + var c = Math.cos(angle); + var s = Math.sin(angle); + + mat[0] = m00 * c + m02 * -s; + mat[1] = m10 * c + m12 * -s; + mat[2] = m20 * c + m22 * -s; + mat[3] = m30 * c + m32 * -s; + mat[8] = m00 * s + m02 * c; + mat[9] = m10 * s + m12 * c; + mat[10] = m20 * s + m22 * c; + mat[11] = m30 * s + m32 * c; + + return mat; +}; + + +/** + * Rotate the given matrix by angle about the z axis. Equivalent to: + * goog.vec.Mat4.multMat( + * mat, + * goog.vec.Mat4.makeRotateZ(goog.vec.Mat4.create(), angle), + * mat); + * + * @param {goog.vec.Mat4.AnyType} mat The matrix. + * @param {number} angle The angle in radians. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.rotateZ = function(mat, angle) { + var m00 = mat[0], m10 = mat[1], m20 = mat[2], m30 = mat[3]; + var m01 = mat[4], m11 = mat[5], m21 = mat[6], m31 = mat[7]; + + var c = Math.cos(angle); + var s = Math.sin(angle); + + mat[0] = m00 * c + m01 * s; + mat[1] = m10 * c + m11 * s; + mat[2] = m20 * c + m21 * s; + mat[3] = m30 * c + m31 * s; + mat[4] = m00 * -s + m01 * c; + mat[5] = m10 * -s + m11 * c; + mat[6] = m20 * -s + m21 * c; + mat[7] = m30 * -s + m31 * c; + + return mat; +}; + + +/** + * Retrieves the translation component of the transformation matrix. + * + * @param {goog.vec.Mat4.AnyType} mat The transformation matrix. + * @param {goog.vec.Vec3.AnyType} translation The vector for storing the + * result. + * @return {goog.vec.Mat4.AnyType} return mat so that operations can be + * chained. + */ +goog.vec.Mat4.getTranslation = function(mat, translation) { + translation[0] = mat[12]; + translation[1] = mat[13]; + translation[2] = mat[14]; + return translation; +}; + + +/** + * @type {!Array<!goog.vec.Vec3.Type>} + * @private + */ +goog.vec.Mat4.tmpVec3_ = + [goog.vec.Vec3.createFloat64(), goog.vec.Vec3.createFloat64()]; + + +/** + * @type {!Array<!goog.vec.Vec4.Type>} + * @private + */ +goog.vec.Mat4.tmpVec4_ = [ + goog.vec.Vec4.createFloat64(), goog.vec.Vec4.createFloat64(), + goog.vec.Vec4.createFloat64() +]; + + +/** + * @type {!Array<!goog.vec.Mat4.Type>} + * @private + */ +goog.vec.Mat4.tmpMat4_ = [goog.vec.Mat4.createFloat64()]; + +goog.provide('ol.geom.flat.transform'); + +goog.require('goog.vec.Mat4'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {Array.<number>=} opt_dest Destination. + * @return {Array.<number>} Transformed coordinates. + */ +ol.geom.flat.transform.transform2D = function(flatCoordinates, offset, end, stride, transform, opt_dest) { + var m00 = goog.vec.Mat4.getElement(transform, 0, 0); + var m10 = goog.vec.Mat4.getElement(transform, 1, 0); + var m01 = goog.vec.Mat4.getElement(transform, 0, 1); + var m11 = goog.vec.Mat4.getElement(transform, 1, 1); + var m03 = goog.vec.Mat4.getElement(transform, 0, 3); + var m13 = goog.vec.Mat4.getElement(transform, 1, 3); + var dest = opt_dest ? opt_dest : []; + var i = 0; + var j; + for (j = offset; j < end; j += stride) { + var x = flatCoordinates[j]; + var y = flatCoordinates[j + 1]; + dest[i++] = m00 * x + m01 * y + m03; + dest[i++] = m10 * x + m11 * y + m13; + } + if (opt_dest && dest.length != i) { + dest.length = i; + } + return dest; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} angle Angle. + * @param {Array.<number>} anchor Rotation anchor point. + * @param {Array.<number>=} opt_dest Destination. + * @return {Array.<number>} Transformed coordinates. + */ +ol.geom.flat.transform.rotate = function(flatCoordinates, offset, end, stride, angle, anchor, opt_dest) { + var dest = opt_dest ? opt_dest : []; + var cos = Math.cos(angle); + var sin = Math.sin(angle); + var anchorX = anchor[0]; + var anchorY = anchor[1]; + var i = 0; + for (var j = offset; j < end; j += stride) { + var deltaX = flatCoordinates[j] - anchorX; + var deltaY = flatCoordinates[j + 1] - anchorY; + dest[i++] = anchorX + deltaX * cos - deltaY * sin; + dest[i++] = anchorY + deltaX * sin + deltaY * cos; + for (var k = j + 2; k < j + stride; ++k) { + dest[i++] = flatCoordinates[k]; + } + } + if (opt_dest && dest.length != i) { + dest.length = i; + } + return dest; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} deltaX Delta X. + * @param {number} deltaY Delta Y. + * @param {Array.<number>=} opt_dest Destination. + * @return {Array.<number>} Transformed coordinates. + */ +ol.geom.flat.transform.translate = function(flatCoordinates, offset, end, stride, deltaX, deltaY, opt_dest) { + var dest = opt_dest ? opt_dest : []; + var i = 0; + var j, k; + for (j = offset; j < end; j += stride) { + dest[i++] = flatCoordinates[j] + deltaX; + dest[i++] = flatCoordinates[j + 1] + deltaY; + for (k = j + 2; k < j + stride; ++k) { + dest[i++] = flatCoordinates[k]; + } + } + if (opt_dest && dest.length != i) { + dest.length = i; + } + return dest; +}; + +goog.provide('ol.geom.SimpleGeometry'); + +goog.require('goog.asserts'); +goog.require('ol.functions'); +goog.require('ol.extent'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.flat.transform'); +goog.require('ol.object'); + + +/** + * @classdesc + * Abstract base class; only used for creating subclasses; do not instantiate + * in apps, as cannot be rendered. + * + * @constructor + * @extends {ol.geom.Geometry} + * @api stable + */ +ol.geom.SimpleGeometry = function() { + + ol.geom.Geometry.call(this); + + /** + * @protected + * @type {ol.geom.GeometryLayout} + */ + this.layout = ol.geom.GeometryLayout.XY; + + /** + * @protected + * @type {number} + */ + this.stride = 2; + + /** + * @protected + * @type {Array.<number>} + */ + this.flatCoordinates = null; + +}; +ol.inherits(ol.geom.SimpleGeometry, ol.geom.Geometry); + + +/** + * @param {number} stride Stride. + * @private + * @return {ol.geom.GeometryLayout} layout Layout. + */ +ol.geom.SimpleGeometry.getLayoutForStride_ = function(stride) { + if (stride == 2) { + return ol.geom.GeometryLayout.XY; + } else if (stride == 3) { + return ol.geom.GeometryLayout.XYZ; + } else if (stride == 4) { + return ol.geom.GeometryLayout.XYZM; + } else { + goog.asserts.fail('unsupported stride: ' + stride); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @return {number} Stride. + */ +ol.geom.SimpleGeometry.getStrideForLayout = function(layout) { + if (layout == ol.geom.GeometryLayout.XY) { + return 2; + } else if (layout == ol.geom.GeometryLayout.XYZ) { + return 3; + } else if (layout == ol.geom.GeometryLayout.XYM) { + return 3; + } else if (layout == ol.geom.GeometryLayout.XYZM) { + return 4; + } else { + goog.asserts.fail('unsupported layout: ' + layout); + } +}; + + +/** + * @inheritDoc + */ +ol.geom.SimpleGeometry.prototype.containsXY = ol.functions.FALSE; + + +/** + * @inheritDoc + */ +ol.geom.SimpleGeometry.prototype.computeExtent = function(extent) { + return ol.extent.createOrUpdateFromFlatCoordinates( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + extent); +}; + + +/** + * @return {Array} Coordinates. + */ +ol.geom.SimpleGeometry.prototype.getCoordinates = goog.abstractMethod; + + +/** + * Return the first coordinate of the geometry. + * @return {ol.Coordinate} First coordinate. + * @api stable + */ +ol.geom.SimpleGeometry.prototype.getFirstCoordinate = function() { + return this.flatCoordinates.slice(0, this.stride); +}; + + +/** + * @return {Array.<number>} Flat coordinates. + */ +ol.geom.SimpleGeometry.prototype.getFlatCoordinates = function() { + return this.flatCoordinates; +}; + + +/** + * Return the last coordinate of the geometry. + * @return {ol.Coordinate} Last point. + * @api stable + */ +ol.geom.SimpleGeometry.prototype.getLastCoordinate = function() { + return this.flatCoordinates.slice(this.flatCoordinates.length - this.stride); +}; + + +/** + * Return the {@link ol.geom.GeometryLayout layout} of the geometry. + * @return {ol.geom.GeometryLayout} Layout. + * @api stable + */ +ol.geom.SimpleGeometry.prototype.getLayout = function() { + return this.layout; +}; + + +/** + * @inheritDoc + */ +ol.geom.SimpleGeometry.prototype.getSimplifiedGeometry = function(squaredTolerance) { + if (this.simplifiedGeometryRevision != this.getRevision()) { + ol.object.clear(this.simplifiedGeometryCache); + this.simplifiedGeometryMaxMinSquaredTolerance = 0; + this.simplifiedGeometryRevision = this.getRevision(); + } + // If squaredTolerance is negative or if we know that simplification will not + // have any effect then just return this. + if (squaredTolerance < 0 || + (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && + squaredTolerance <= this.simplifiedGeometryMaxMinSquaredTolerance)) { + return this; + } + var key = squaredTolerance.toString(); + if (this.simplifiedGeometryCache.hasOwnProperty(key)) { + return this.simplifiedGeometryCache[key]; + } else { + var simplifiedGeometry = + this.getSimplifiedGeometryInternal(squaredTolerance); + var simplifiedFlatCoordinates = simplifiedGeometry.getFlatCoordinates(); + if (simplifiedFlatCoordinates.length < this.flatCoordinates.length) { + this.simplifiedGeometryCache[key] = simplifiedGeometry; + return simplifiedGeometry; + } else { + // Simplification did not actually remove any coordinates. We now know + // that any calls to getSimplifiedGeometry with a squaredTolerance less + // than or equal to the current squaredTolerance will also not have any + // effect. This allows us to short circuit simplification (saving CPU + // cycles) and prevents the cache of simplified geometries from filling + // up with useless identical copies of this geometry (saving memory). + this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; + return this; + } + } +}; + + +/** + * @param {number} squaredTolerance Squared tolerance. + * @return {ol.geom.SimpleGeometry} Simplified geometry. + * @protected + */ +ol.geom.SimpleGeometry.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { + return this; +}; + + +/** + * @return {number} Stride. + */ +ol.geom.SimpleGeometry.prototype.getStride = function() { + return this.stride; +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @protected + */ +ol.geom.SimpleGeometry.prototype.setFlatCoordinatesInternal = function(layout, flatCoordinates) { + this.stride = ol.geom.SimpleGeometry.getStrideForLayout(layout); + this.layout = layout; + this.flatCoordinates = flatCoordinates; +}; + + +/** + * @param {Array} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + */ +ol.geom.SimpleGeometry.prototype.setCoordinates = goog.abstractMethod; + + +/** + * @param {ol.geom.GeometryLayout|undefined} layout Layout. + * @param {Array} coordinates Coordinates. + * @param {number} nesting Nesting. + * @protected + */ +ol.geom.SimpleGeometry.prototype.setLayout = function(layout, coordinates, nesting) { + /** @type {number} */ + var stride; + if (layout) { + stride = ol.geom.SimpleGeometry.getStrideForLayout(layout); + } else { + var i; + for (i = 0; i < nesting; ++i) { + if (coordinates.length === 0) { + this.layout = ol.geom.GeometryLayout.XY; + this.stride = 2; + return; + } else { + coordinates = /** @type {Array} */ (coordinates[0]); + } + } + stride = coordinates.length; + layout = ol.geom.SimpleGeometry.getLayoutForStride_(stride); + } + this.layout = layout; + this.stride = stride; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.SimpleGeometry.prototype.applyTransform = function(transformFn) { + if (this.flatCoordinates) { + transformFn(this.flatCoordinates, this.flatCoordinates, this.stride); + this.changed(); + } +}; + + +/** + * @inheritDoc + * @api + */ +ol.geom.SimpleGeometry.prototype.rotate = function(angle, anchor) { + var flatCoordinates = this.getFlatCoordinates(); + if (flatCoordinates) { + var stride = this.getStride(); + ol.geom.flat.transform.rotate( + flatCoordinates, 0, flatCoordinates.length, + stride, angle, anchor, flatCoordinates); + this.changed(); + } +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.SimpleGeometry.prototype.translate = function(deltaX, deltaY) { + var flatCoordinates = this.getFlatCoordinates(); + if (flatCoordinates) { + var stride = this.getStride(); + ol.geom.flat.transform.translate( + flatCoordinates, 0, flatCoordinates.length, stride, + deltaX, deltaY, flatCoordinates); + this.changed(); + } +}; + + +/** + * @param {ol.geom.SimpleGeometry} simpleGeometry Simple geometry. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {Array.<number>=} opt_dest Destination. + * @return {Array.<number>} Transformed flat coordinates. + */ +ol.geom.transformSimpleGeometry2D = function(simpleGeometry, transform, opt_dest) { + var flatCoordinates = simpleGeometry.getFlatCoordinates(); + if (!flatCoordinates) { + return null; + } else { + var stride = simpleGeometry.getStride(); + return ol.geom.flat.transform.transform2D( + flatCoordinates, 0, flatCoordinates.length, stride, + transform, opt_dest); + } +}; + +goog.provide('ol.geom.flat.area'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {number} Area. + */ +ol.geom.flat.area.linearRing = function(flatCoordinates, offset, end, stride) { + var twiceArea = 0; + var x1 = flatCoordinates[end - stride]; + var y1 = flatCoordinates[end - stride + 1]; + for (; offset < end; offset += stride) { + var x2 = flatCoordinates[offset]; + var y2 = flatCoordinates[offset + 1]; + twiceArea += y1 * x2 - x1 * y2; + x1 = x2; + y1 = y2; + } + return twiceArea / 2; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @return {number} Area. + */ +ol.geom.flat.area.linearRings = function(flatCoordinates, offset, ends, stride) { + var area = 0; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + area += ol.geom.flat.area.linearRing(flatCoordinates, offset, end, stride); + offset = end; + } + return area; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @return {number} Area. + */ +ol.geom.flat.area.linearRingss = function(flatCoordinates, offset, endss, stride) { + var area = 0; + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + area += + ol.geom.flat.area.linearRings(flatCoordinates, offset, ends, stride); + offset = ends[ends.length - 1]; + } + return area; +}; + +goog.provide('ol.geom.flat.closest'); + +goog.require('goog.asserts'); +goog.require('ol.math'); + + +/** + * Returns the point on the 2D line segment flatCoordinates[offset1] to + * flatCoordinates[offset2] that is closest to the point (x, y). Extra + * dimensions are linearly interpolated. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset1 Offset 1. + * @param {number} offset2 Offset 2. + * @param {number} stride Stride. + * @param {number} x X. + * @param {number} y Y. + * @param {Array.<number>} closestPoint Closest point. + */ +ol.geom.flat.closest.point = function(flatCoordinates, offset1, offset2, stride, x, y, closestPoint) { + var x1 = flatCoordinates[offset1]; + var y1 = flatCoordinates[offset1 + 1]; + var dx = flatCoordinates[offset2] - x1; + var dy = flatCoordinates[offset2 + 1] - y1; + var i, offset; + if (dx === 0 && dy === 0) { + offset = offset1; + } else { + var t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); + if (t > 1) { + offset = offset2; + } else if (t > 0) { + for (i = 0; i < stride; ++i) { + closestPoint[i] = ol.math.lerp(flatCoordinates[offset1 + i], + flatCoordinates[offset2 + i], t); + } + closestPoint.length = stride; + return; + } else { + offset = offset1; + } + } + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[offset + i]; + } + closestPoint.length = stride; +}; + + +/** + * Return the squared of the largest distance between any pair of consecutive + * coordinates. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} maxSquaredDelta Max squared delta. + * @return {number} Max squared delta. + */ +ol.geom.flat.closest.getMaxSquaredDelta = function(flatCoordinates, offset, end, stride, maxSquaredDelta) { + var x1 = flatCoordinates[offset]; + var y1 = flatCoordinates[offset + 1]; + for (offset += stride; offset < end; offset += stride) { + var x2 = flatCoordinates[offset]; + var y2 = flatCoordinates[offset + 1]; + var squaredDelta = ol.math.squaredDistance(x1, y1, x2, y2); + if (squaredDelta > maxSquaredDelta) { + maxSquaredDelta = squaredDelta; + } + x1 = x2; + y1 = y2; + } + return maxSquaredDelta; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {number} maxSquaredDelta Max squared delta. + * @return {number} Max squared delta. + */ +ol.geom.flat.closest.getsMaxSquaredDelta = function(flatCoordinates, offset, ends, stride, maxSquaredDelta) { + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + maxSquaredDelta = ol.geom.flat.closest.getMaxSquaredDelta( + flatCoordinates, offset, end, stride, maxSquaredDelta); + offset = end; + } + return maxSquaredDelta; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {number} maxSquaredDelta Max squared delta. + * @return {number} Max squared delta. + */ +ol.geom.flat.closest.getssMaxSquaredDelta = function(flatCoordinates, offset, endss, stride, maxSquaredDelta) { + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + maxSquaredDelta = ol.geom.flat.closest.getsMaxSquaredDelta( + flatCoordinates, offset, ends, stride, maxSquaredDelta); + offset = ends[ends.length - 1]; + } + return maxSquaredDelta; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} maxDelta Max delta. + * @param {boolean} isRing Is ring. + * @param {number} x X. + * @param {number} y Y. + * @param {Array.<number>} closestPoint Closest point. + * @param {number} minSquaredDistance Minimum squared distance. + * @param {Array.<number>=} opt_tmpPoint Temporary point object. + * @return {number} Minimum squared distance. + */ +ol.geom.flat.closest.getClosestPoint = function(flatCoordinates, offset, end, + stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, + opt_tmpPoint) { + if (offset == end) { + return minSquaredDistance; + } + var i, squaredDistance; + if (maxDelta === 0) { + // All points are identical, so just test the first point. + squaredDistance = ol.math.squaredDistance( + x, y, flatCoordinates[offset], flatCoordinates[offset + 1]); + if (squaredDistance < minSquaredDistance) { + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[offset + i]; + } + closestPoint.length = stride; + return squaredDistance; + } else { + return minSquaredDistance; + } + } + goog.asserts.assert(maxDelta > 0, 'maxDelta should be larger than 0'); + var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN]; + var index = offset + stride; + while (index < end) { + ol.geom.flat.closest.point( + flatCoordinates, index - stride, index, stride, x, y, tmpPoint); + squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]); + if (squaredDistance < minSquaredDistance) { + minSquaredDistance = squaredDistance; + for (i = 0; i < stride; ++i) { + closestPoint[i] = tmpPoint[i]; + } + closestPoint.length = stride; + index += stride; + } else { + // Skip ahead multiple points, because we know that all the skipped + // points cannot be any closer than the closest point we have found so + // far. We know this because we know how close the current point is, how + // close the closest point we have found so far is, and the maximum + // distance between consecutive points. For example, if we're currently + // at distance 10, the best we've found so far is 3, and that the maximum + // distance between consecutive points is 2, then we'll need to skip at + // least (10 - 3) / 2 == 3 (rounded down) points to have any chance of + // finding a closer point. We use Math.max(..., 1) to ensure that we + // always advance at least one point, to avoid an infinite loop. + index += stride * Math.max( + ((Math.sqrt(squaredDistance) - + Math.sqrt(minSquaredDistance)) / maxDelta) | 0, 1); + } + } + if (isRing) { + // Check the closing segment. + ol.geom.flat.closest.point( + flatCoordinates, end - stride, offset, stride, x, y, tmpPoint); + squaredDistance = ol.math.squaredDistance(x, y, tmpPoint[0], tmpPoint[1]); + if (squaredDistance < minSquaredDistance) { + minSquaredDistance = squaredDistance; + for (i = 0; i < stride; ++i) { + closestPoint[i] = tmpPoint[i]; + } + closestPoint.length = stride; + } + } + return minSquaredDistance; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {number} maxDelta Max delta. + * @param {boolean} isRing Is ring. + * @param {number} x X. + * @param {number} y Y. + * @param {Array.<number>} closestPoint Closest point. + * @param {number} minSquaredDistance Minimum squared distance. + * @param {Array.<number>=} opt_tmpPoint Temporary point object. + * @return {number} Minimum squared distance. + */ +ol.geom.flat.closest.getsClosestPoint = function(flatCoordinates, offset, ends, + stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, + opt_tmpPoint) { + var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN]; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + minSquaredDistance = ol.geom.flat.closest.getClosestPoint( + flatCoordinates, offset, end, stride, + maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint); + offset = end; + } + return minSquaredDistance; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {number} maxDelta Max delta. + * @param {boolean} isRing Is ring. + * @param {number} x X. + * @param {number} y Y. + * @param {Array.<number>} closestPoint Closest point. + * @param {number} minSquaredDistance Minimum squared distance. + * @param {Array.<number>=} opt_tmpPoint Temporary point object. + * @return {number} Minimum squared distance. + */ +ol.geom.flat.closest.getssClosestPoint = function(flatCoordinates, offset, + endss, stride, maxDelta, isRing, x, y, closestPoint, minSquaredDistance, + opt_tmpPoint) { + var tmpPoint = opt_tmpPoint ? opt_tmpPoint : [NaN, NaN]; + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + minSquaredDistance = ol.geom.flat.closest.getsClosestPoint( + flatCoordinates, offset, ends, stride, + maxDelta, isRing, x, y, closestPoint, minSquaredDistance, tmpPoint); + offset = ends[ends.length - 1]; + } + return minSquaredDistance; +}; + +goog.provide('ol.geom.flat.deflate'); + +goog.require('goog.asserts'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} stride Stride. + * @return {number} offset Offset. + */ +ol.geom.flat.deflate.coordinate = function(flatCoordinates, offset, coordinate, stride) { + goog.asserts.assert(coordinate.length == stride, + 'length of the coordinate array should match stride'); + var i, ii; + for (i = 0, ii = coordinate.length; i < ii; ++i) { + flatCoordinates[offset++] = coordinate[i]; + } + return offset; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {number} stride Stride. + * @return {number} offset Offset. + */ +ol.geom.flat.deflate.coordinates = function(flatCoordinates, offset, coordinates, stride) { + var i, ii; + for (i = 0, ii = coordinates.length; i < ii; ++i) { + var coordinate = coordinates[i]; + goog.asserts.assert(coordinate.length == stride, + 'length of coordinate array should match stride'); + var j; + for (j = 0; j < stride; ++j) { + flatCoordinates[offset++] = coordinate[j]; + } + } + return offset; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<ol.Coordinate>>} coordinatess Coordinatess. + * @param {number} stride Stride. + * @param {Array.<number>=} opt_ends Ends. + * @return {Array.<number>} Ends. + */ +ol.geom.flat.deflate.coordinatess = function(flatCoordinates, offset, coordinatess, stride, opt_ends) { + var ends = opt_ends ? opt_ends : []; + var i = 0; + var j, jj; + for (j = 0, jj = coordinatess.length; j < jj; ++j) { + var end = ol.geom.flat.deflate.coordinates( + flatCoordinates, offset, coordinatess[j], stride); + ends[i++] = end; + offset = end; + } + ends.length = i; + return ends; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinatesss Coordinatesss. + * @param {number} stride Stride. + * @param {Array.<Array.<number>>=} opt_endss Endss. + * @return {Array.<Array.<number>>} Endss. + */ +ol.geom.flat.deflate.coordinatesss = function(flatCoordinates, offset, coordinatesss, stride, opt_endss) { + var endss = opt_endss ? opt_endss : []; + var i = 0; + var j, jj; + for (j = 0, jj = coordinatesss.length; j < jj; ++j) { + var ends = ol.geom.flat.deflate.coordinatess( + flatCoordinates, offset, coordinatesss[j], stride, endss[i]); + endss[i++] = ends; + offset = ends[ends.length - 1]; + } + endss.length = i; + return endss; +}; + +goog.provide('ol.geom.flat.inflate'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {Array.<ol.Coordinate>=} opt_coordinates Coordinates. + * @return {Array.<ol.Coordinate>} Coordinates. + */ +ol.geom.flat.inflate.coordinates = function(flatCoordinates, offset, end, stride, opt_coordinates) { + var coordinates = opt_coordinates !== undefined ? opt_coordinates : []; + var i = 0; + var j; + for (j = offset; j < end; j += stride) { + coordinates[i++] = flatCoordinates.slice(j, j + stride); + } + coordinates.length = i; + return coordinates; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {Array.<Array.<ol.Coordinate>>=} opt_coordinatess Coordinatess. + * @return {Array.<Array.<ol.Coordinate>>} Coordinatess. + */ +ol.geom.flat.inflate.coordinatess = function(flatCoordinates, offset, ends, stride, opt_coordinatess) { + var coordinatess = opt_coordinatess !== undefined ? opt_coordinatess : []; + var i = 0; + var j, jj; + for (j = 0, jj = ends.length; j < jj; ++j) { + var end = ends[j]; + coordinatess[i++] = ol.geom.flat.inflate.coordinates( + flatCoordinates, offset, end, stride, coordinatess[i]); + offset = end; + } + coordinatess.length = i; + return coordinatess; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {Array.<Array.<Array.<ol.Coordinate>>>=} opt_coordinatesss + * Coordinatesss. + * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinatesss. + */ +ol.geom.flat.inflate.coordinatesss = function(flatCoordinates, offset, endss, stride, opt_coordinatesss) { + var coordinatesss = opt_coordinatesss !== undefined ? opt_coordinatesss : []; + var i = 0; + var j, jj; + for (j = 0, jj = endss.length; j < jj; ++j) { + var ends = endss[j]; + coordinatesss[i++] = ol.geom.flat.inflate.coordinatess( + flatCoordinates, offset, ends, stride, coordinatesss[i]); + offset = ends[ends.length - 1]; + } + coordinatesss.length = i; + return coordinatesss; +}; + +// Based on simplify-js https://github.com/mourner/simplify-js +// Copyright (c) 2012, Vladimir Agafonkin +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. 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. +// +// 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 HOLDER 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. + +goog.provide('ol.geom.flat.simplify'); + +goog.require('ol.math'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} squaredTolerance Squared tolerance. + * @param {boolean} highQuality Highest quality. + * @param {Array.<number>=} opt_simplifiedFlatCoordinates Simplified flat + * coordinates. + * @return {Array.<number>} Simplified line string. + */ +ol.geom.flat.simplify.lineString = function(flatCoordinates, offset, end, + stride, squaredTolerance, highQuality, opt_simplifiedFlatCoordinates) { + var simplifiedFlatCoordinates = opt_simplifiedFlatCoordinates !== undefined ? + opt_simplifiedFlatCoordinates : []; + if (!highQuality) { + end = ol.geom.flat.simplify.radialDistance(flatCoordinates, offset, end, + stride, squaredTolerance, + simplifiedFlatCoordinates, 0); + flatCoordinates = simplifiedFlatCoordinates; + offset = 0; + stride = 2; + } + simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker( + flatCoordinates, offset, end, stride, squaredTolerance, + simplifiedFlatCoordinates, 0); + return simplifiedFlatCoordinates; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} squaredTolerance Squared tolerance. + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat + * coordinates. + * @param {number} simplifiedOffset Simplified offset. + * @return {number} Simplified offset. + */ +ol.geom.flat.simplify.douglasPeucker = function(flatCoordinates, offset, end, + stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) { + var n = (end - offset) / stride; + if (n < 3) { + for (; offset < end; offset += stride) { + simplifiedFlatCoordinates[simplifiedOffset++] = + flatCoordinates[offset]; + simplifiedFlatCoordinates[simplifiedOffset++] = + flatCoordinates[offset + 1]; + } + return simplifiedOffset; + } + /** @type {Array.<number>} */ + var markers = new Array(n); + markers[0] = 1; + markers[n - 1] = 1; + /** @type {Array.<number>} */ + var stack = [offset, end - stride]; + var index = 0; + var i; + while (stack.length > 0) { + var last = stack.pop(); + var first = stack.pop(); + var maxSquaredDistance = 0; + var x1 = flatCoordinates[first]; + var y1 = flatCoordinates[first + 1]; + var x2 = flatCoordinates[last]; + var y2 = flatCoordinates[last + 1]; + for (i = first + stride; i < last; i += stride) { + var x = flatCoordinates[i]; + var y = flatCoordinates[i + 1]; + var squaredDistance = ol.math.squaredSegmentDistance( + x, y, x1, y1, x2, y2); + if (squaredDistance > maxSquaredDistance) { + index = i; + maxSquaredDistance = squaredDistance; + } + } + if (maxSquaredDistance > squaredTolerance) { + markers[(index - offset) / stride] = 1; + if (first + stride < index) { + stack.push(first, index); + } + if (index + stride < last) { + stack.push(index, last); + } + } + } + for (i = 0; i < n; ++i) { + if (markers[i]) { + simplifiedFlatCoordinates[simplifiedOffset++] = + flatCoordinates[offset + i * stride]; + simplifiedFlatCoordinates[simplifiedOffset++] = + flatCoordinates[offset + i * stride + 1]; + } + } + return simplifiedOffset; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {number} squaredTolerance Squared tolerance. + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat + * coordinates. + * @param {number} simplifiedOffset Simplified offset. + * @param {Array.<number>} simplifiedEnds Simplified ends. + * @return {number} Simplified offset. + */ +ol.geom.flat.simplify.douglasPeuckers = function(flatCoordinates, offset, + ends, stride, squaredTolerance, simplifiedFlatCoordinates, + simplifiedOffset, simplifiedEnds) { + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + simplifiedOffset = ol.geom.flat.simplify.douglasPeucker( + flatCoordinates, offset, end, stride, squaredTolerance, + simplifiedFlatCoordinates, simplifiedOffset); + simplifiedEnds.push(simplifiedOffset); + offset = end; + } + return simplifiedOffset; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {number} squaredTolerance Squared tolerance. + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat + * coordinates. + * @param {number} simplifiedOffset Simplified offset. + * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss. + * @return {number} Simplified offset. + */ +ol.geom.flat.simplify.douglasPeuckerss = function( + flatCoordinates, offset, endss, stride, squaredTolerance, + simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) { + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + var simplifiedEnds = []; + simplifiedOffset = ol.geom.flat.simplify.douglasPeuckers( + flatCoordinates, offset, ends, stride, squaredTolerance, + simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds); + simplifiedEndss.push(simplifiedEnds); + offset = ends[ends.length - 1]; + } + return simplifiedOffset; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} squaredTolerance Squared tolerance. + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat + * coordinates. + * @param {number} simplifiedOffset Simplified offset. + * @return {number} Simplified offset. + */ +ol.geom.flat.simplify.radialDistance = function(flatCoordinates, offset, end, + stride, squaredTolerance, simplifiedFlatCoordinates, simplifiedOffset) { + if (end <= offset + stride) { + // zero or one point, no simplification possible, so copy and return + for (; offset < end; offset += stride) { + simplifiedFlatCoordinates[simplifiedOffset++] = flatCoordinates[offset]; + simplifiedFlatCoordinates[simplifiedOffset++] = + flatCoordinates[offset + 1]; + } + return simplifiedOffset; + } + var x1 = flatCoordinates[offset]; + var y1 = flatCoordinates[offset + 1]; + // copy first point + simplifiedFlatCoordinates[simplifiedOffset++] = x1; + simplifiedFlatCoordinates[simplifiedOffset++] = y1; + var x2 = x1; + var y2 = y1; + for (offset += stride; offset < end; offset += stride) { + x2 = flatCoordinates[offset]; + y2 = flatCoordinates[offset + 1]; + if (ol.math.squaredDistance(x1, y1, x2, y2) > squaredTolerance) { + // copy point at offset + simplifiedFlatCoordinates[simplifiedOffset++] = x2; + simplifiedFlatCoordinates[simplifiedOffset++] = y2; + x1 = x2; + y1 = y2; + } + } + if (x2 != x1 || y2 != y1) { + // copy last point + simplifiedFlatCoordinates[simplifiedOffset++] = x2; + simplifiedFlatCoordinates[simplifiedOffset++] = y2; + } + return simplifiedOffset; +}; + + +/** + * @param {number} value Value. + * @param {number} tolerance Tolerance. + * @return {number} Rounded value. + */ +ol.geom.flat.simplify.snap = function(value, tolerance) { + return tolerance * Math.round(value / tolerance); +}; + + +/** + * Simplifies a line string using an algorithm designed by Tim Schaub. + * Coordinates are snapped to the nearest value in a virtual grid and + * consecutive duplicate coordinates are discarded. This effectively preserves + * topology as the simplification of any subsection of a line string is + * independent of the rest of the line string. This means that, for examples, + * the common edge between two polygons will be simplified to the same line + * string independently in both polygons. This implementation uses a single + * pass over the coordinates and eliminates intermediate collinear points. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} tolerance Tolerance. + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat + * coordinates. + * @param {number} simplifiedOffset Simplified offset. + * @return {number} Simplified offset. + */ +ol.geom.flat.simplify.quantize = function(flatCoordinates, offset, end, stride, + tolerance, simplifiedFlatCoordinates, simplifiedOffset) { + // do nothing if the line is empty + if (offset == end) { + return simplifiedOffset; + } + // snap the first coordinate (P1) + var x1 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance); + var y1 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance); + offset += stride; + // add the first coordinate to the output + simplifiedFlatCoordinates[simplifiedOffset++] = x1; + simplifiedFlatCoordinates[simplifiedOffset++] = y1; + // find the next coordinate that does not snap to the same value as the first + // coordinate (P2) + var x2, y2; + do { + x2 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance); + y2 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance); + offset += stride; + if (offset == end) { + // all coordinates snap to the same value, the line collapses to a point + // push the last snapped value anyway to ensure that the output contains + // at least two points + // FIXME should we really return at least two points anyway? + simplifiedFlatCoordinates[simplifiedOffset++] = x2; + simplifiedFlatCoordinates[simplifiedOffset++] = y2; + return simplifiedOffset; + } + } while (x2 == x1 && y2 == y1); + while (offset < end) { + var x3, y3; + // snap the next coordinate (P3) + x3 = ol.geom.flat.simplify.snap(flatCoordinates[offset], tolerance); + y3 = ol.geom.flat.simplify.snap(flatCoordinates[offset + 1], tolerance); + offset += stride; + // skip P3 if it is equal to P2 + if (x3 == x2 && y3 == y2) { + continue; + } + // calculate the delta between P1 and P2 + var dx1 = x2 - x1; + var dy1 = y2 - y1; + // calculate the delta between P3 and P1 + var dx2 = x3 - x1; + var dy2 = y3 - y1; + // if P1, P2, and P3 are colinear and P3 is further from P1 than P2 is from + // P1 in the same direction then P2 is on the straight line between P1 and + // P3 + if ((dx1 * dy2 == dy1 * dx2) && + ((dx1 < 0 && dx2 < dx1) || dx1 == dx2 || (dx1 > 0 && dx2 > dx1)) && + ((dy1 < 0 && dy2 < dy1) || dy1 == dy2 || (dy1 > 0 && dy2 > dy1))) { + // discard P2 and set P2 = P3 + x2 = x3; + y2 = y3; + continue; + } + // either P1, P2, and P3 are not colinear, or they are colinear but P3 is + // between P3 and P1 or on the opposite half of the line to P2. add P2, + // and continue with P1 = P2 and P2 = P3 + simplifiedFlatCoordinates[simplifiedOffset++] = x2; + simplifiedFlatCoordinates[simplifiedOffset++] = y2; + x1 = x2; + y1 = y2; + x2 = x3; + y2 = y3; + } + // add the last point (P2) + simplifiedFlatCoordinates[simplifiedOffset++] = x2; + simplifiedFlatCoordinates[simplifiedOffset++] = y2; + return simplifiedOffset; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {number} tolerance Tolerance. + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat + * coordinates. + * @param {number} simplifiedOffset Simplified offset. + * @param {Array.<number>} simplifiedEnds Simplified ends. + * @return {number} Simplified offset. + */ +ol.geom.flat.simplify.quantizes = function( + flatCoordinates, offset, ends, stride, + tolerance, + simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds) { + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + simplifiedOffset = ol.geom.flat.simplify.quantize( + flatCoordinates, offset, end, stride, + tolerance, + simplifiedFlatCoordinates, simplifiedOffset); + simplifiedEnds.push(simplifiedOffset); + offset = end; + } + return simplifiedOffset; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {number} tolerance Tolerance. + * @param {Array.<number>} simplifiedFlatCoordinates Simplified flat + * coordinates. + * @param {number} simplifiedOffset Simplified offset. + * @param {Array.<Array.<number>>} simplifiedEndss Simplified endss. + * @return {number} Simplified offset. + */ +ol.geom.flat.simplify.quantizess = function( + flatCoordinates, offset, endss, stride, + tolerance, + simplifiedFlatCoordinates, simplifiedOffset, simplifiedEndss) { + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + var simplifiedEnds = []; + simplifiedOffset = ol.geom.flat.simplify.quantizes( + flatCoordinates, offset, ends, stride, + tolerance, + simplifiedFlatCoordinates, simplifiedOffset, simplifiedEnds); + simplifiedEndss.push(simplifiedEnds); + offset = ends[ends.length - 1]; + } + return simplifiedOffset; +}; + +goog.provide('ol.geom.LinearRing'); + +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.area'); +goog.require('ol.geom.flat.closest'); +goog.require('ol.geom.flat.deflate'); +goog.require('ol.geom.flat.inflate'); +goog.require('ol.geom.flat.simplify'); + + +/** + * @classdesc + * Linear ring geometry. Only used as part of polygon; cannot be rendered + * on its own. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.LinearRing = function(coordinates, opt_layout) { + + ol.geom.SimpleGeometry.call(this); + + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; + + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; + + this.setCoordinates(coordinates, opt_layout); + +}; +ol.inherits(ol.geom.LinearRing, ol.geom.SimpleGeometry); + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.LinearRing} Clone. + * @api stable + */ +ol.geom.LinearRing.prototype.clone = function() { + var linearRing = new ol.geom.LinearRing(null); + linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); + return linearRing; +}; + + +/** + * @inheritDoc + */ +ol.geom.LinearRing.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return ol.geom.flat.closest.getClosestPoint( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); +}; + + +/** + * Return the area of the linear ring on projected plane. + * @return {number} Area (on projected plane). + * @api stable + */ +ol.geom.LinearRing.prototype.getArea = function() { + return ol.geom.flat.area.linearRing( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); +}; + + +/** + * Return the coordinates of the linear ring. + * @return {Array.<ol.Coordinate>} Coordinates. + * @api stable + */ +ol.geom.LinearRing.prototype.getCoordinates = function() { + return ol.geom.flat.inflate.coordinates( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); +}; + + +/** + * @inheritDoc + */ +ol.geom.LinearRing.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { + var simplifiedFlatCoordinates = []; + simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + squaredTolerance, simplifiedFlatCoordinates, 0); + var simplifiedLinearRing = new ol.geom.LinearRing(null); + simplifiedLinearRing.setFlatCoordinates( + ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates); + return simplifiedLinearRing; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.LinearRing.prototype.getType = function() { + return ol.geom.GeometryType.LINEAR_RING; +}; + + +/** + * Set the coordinates of the linear ring. + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.LinearRing.prototype.setCoordinates = function(coordinates, opt_layout) { + if (!coordinates) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); + } else { + this.setLayout(opt_layout, coordinates, 1); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = ol.geom.flat.deflate.coordinates( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + */ +ol.geom.LinearRing.prototype.setFlatCoordinates = function(layout, flatCoordinates) { + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.changed(); +}; + +goog.provide('ol.geom.Point'); + +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.deflate'); +goog.require('ol.math'); + + +/** + * @classdesc + * Point geometry. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {ol.Coordinate} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.Point = function(coordinates, opt_layout) { + ol.geom.SimpleGeometry.call(this); + this.setCoordinates(coordinates, opt_layout); +}; +ol.inherits(ol.geom.Point, ol.geom.SimpleGeometry); + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.Point} Clone. + * @api stable + */ +ol.geom.Point.prototype.clone = function() { + var point = new ol.geom.Point(null); + point.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); + return point; +}; + + +/** + * @inheritDoc + */ +ol.geom.Point.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + var flatCoordinates = this.flatCoordinates; + var squaredDistance = ol.math.squaredDistance( + x, y, flatCoordinates[0], flatCoordinates[1]); + if (squaredDistance < minSquaredDistance) { + var stride = this.stride; + var i; + for (i = 0; i < stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } + closestPoint.length = stride; + return squaredDistance; + } else { + return minSquaredDistance; + } +}; + + +/** + * Return the coordinate of the point. + * @return {ol.Coordinate} Coordinates. + * @api stable + */ +ol.geom.Point.prototype.getCoordinates = function() { + return !this.flatCoordinates ? [] : this.flatCoordinates.slice(); +}; + + +/** + * @inheritDoc + */ +ol.geom.Point.prototype.computeExtent = function(extent) { + return ol.extent.createOrUpdateFromCoordinate(this.flatCoordinates, extent); +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.Point.prototype.getType = function() { + return ol.geom.GeometryType.POINT; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.Point.prototype.intersectsExtent = function(extent) { + return ol.extent.containsXY(extent, + this.flatCoordinates[0], this.flatCoordinates[1]); +}; + + +/** + * Set the coordinate of the point. + * @param {ol.Coordinate} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.Point.prototype.setCoordinates = function(coordinates, opt_layout) { + if (!coordinates) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); + } else { + this.setLayout(opt_layout, coordinates, 0); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = ol.geom.flat.deflate.coordinate( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + */ +ol.geom.Point.prototype.setFlatCoordinates = function(layout, flatCoordinates) { + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.changed(); +}; + +goog.provide('ol.geom.flat.contains'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} Contains extent. + */ +ol.geom.flat.contains.linearRingContainsExtent = function(flatCoordinates, offset, end, stride, extent) { + var outside = ol.extent.forEachCorner(extent, + /** + * @param {ol.Coordinate} coordinate Coordinate. + * @return {boolean} Contains (x, y). + */ + function(coordinate) { + return !ol.geom.flat.contains.linearRingContainsXY(flatCoordinates, + offset, end, stride, coordinate[0], coordinate[1]); + }); + return !outside; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} x X. + * @param {number} y Y. + * @return {boolean} Contains (x, y). + */ +ol.geom.flat.contains.linearRingContainsXY = function(flatCoordinates, offset, end, stride, x, y) { + // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + var contains = false; + var x1 = flatCoordinates[end - stride]; + var y1 = flatCoordinates[end - stride + 1]; + for (; offset < end; offset += stride) { + var x2 = flatCoordinates[offset]; + var y2 = flatCoordinates[offset + 1]; + var intersect = ((y1 > y) != (y2 > y)) && + (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1); + if (intersect) { + contains = !contains; + } + x1 = x2; + y1 = y2; + } + return contains; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {number} x X. + * @param {number} y Y. + * @return {boolean} Contains (x, y). + */ +ol.geom.flat.contains.linearRingsContainsXY = function(flatCoordinates, offset, ends, stride, x, y) { + goog.asserts.assert(ends.length > 0, 'ends should not be an empty array'); + if (ends.length === 0) { + return false; + } + if (!ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, ends[0], stride, x, y)) { + return false; + } + var i, ii; + for (i = 1, ii = ends.length; i < ii; ++i) { + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, ends[i - 1], ends[i], stride, x, y)) { + return false; + } + } + return true; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {number} x X. + * @param {number} y Y. + * @return {boolean} Contains (x, y). + */ +ol.geom.flat.contains.linearRingssContainsXY = function(flatCoordinates, offset, endss, stride, x, y) { + goog.asserts.assert(endss.length > 0, 'endss should not be an empty array'); + if (endss.length === 0) { + return false; + } + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + if (ol.geom.flat.contains.linearRingsContainsXY( + flatCoordinates, offset, ends, stride, x, y)) { + return true; + } + offset = ends[ends.length - 1]; + } + return false; +}; + +goog.provide('ol.geom.flat.interiorpoint'); + +goog.require('goog.asserts'); +goog.require('ol.array'); +goog.require('ol.geom.flat.contains'); + + +/** + * Calculates a point that is likely to lie in the interior of the linear rings. + * Inspired by JTS's com.vividsolutions.jts.geom.Geometry#getInteriorPoint. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {Array.<number>} flatCenters Flat centers. + * @param {number} flatCentersOffset Flat center offset. + * @param {Array.<number>=} opt_dest Destination. + * @return {Array.<number>} Destination. + */ +ol.geom.flat.interiorpoint.linearRings = function(flatCoordinates, offset, + ends, stride, flatCenters, flatCentersOffset, opt_dest) { + var i, ii, x, x1, x2, y1, y2; + var y = flatCenters[flatCentersOffset + 1]; + /** @type {Array.<number>} */ + var intersections = []; + // Calculate intersections with the horizontal line + var end = ends[0]; + x1 = flatCoordinates[end - stride]; + y1 = flatCoordinates[end - stride + 1]; + for (i = offset; i < end; i += stride) { + x2 = flatCoordinates[i]; + y2 = flatCoordinates[i + 1]; + if ((y <= y1 && y2 <= y) || (y1 <= y && y <= y2)) { + x = (y - y1) / (y2 - y1) * (x2 - x1) + x1; + intersections.push(x); + } + x1 = x2; + y1 = y2; + } + // Find the longest segment of the horizontal line that has its center point + // inside the linear ring. + var pointX = NaN; + var maxSegmentLength = -Infinity; + intersections.sort(ol.array.numberSafeCompareFunction); + x1 = intersections[0]; + for (i = 1, ii = intersections.length; i < ii; ++i) { + x2 = intersections[i]; + var segmentLength = Math.abs(x2 - x1); + if (segmentLength > maxSegmentLength) { + x = (x1 + x2) / 2; + if (ol.geom.flat.contains.linearRingsContainsXY( + flatCoordinates, offset, ends, stride, x, y)) { + pointX = x; + maxSegmentLength = segmentLength; + } + } + x1 = x2; + } + if (isNaN(pointX)) { + // There is no horizontal line that has its center point inside the linear + // ring. Use the center of the the linear ring's extent. + pointX = flatCenters[flatCentersOffset]; + } + if (opt_dest) { + opt_dest.push(pointX, y); + return opt_dest; + } else { + return [pointX, y]; + } +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {Array.<number>} flatCenters Flat centers. + * @return {Array.<number>} Interior points. + */ +ol.geom.flat.interiorpoint.linearRingss = function(flatCoordinates, offset, endss, stride, flatCenters) { + goog.asserts.assert(2 * endss.length == flatCenters.length, + 'endss.length times 2 should be flatCenters.length'); + var interiorPoints = []; + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + interiorPoints = ol.geom.flat.interiorpoint.linearRings(flatCoordinates, + offset, ends, stride, flatCenters, 2 * i, interiorPoints); + offset = ends[ends.length - 1]; + } + return interiorPoints; +}; + +goog.provide('ol.geom.flat.segments'); + + +/** + * This function calls `callback` for each segment of the flat coordinates + * array. If the callback returns a truthy value the function returns that + * value immediately. Otherwise the function returns `false`. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function + * called for each segment. + * @param {S=} opt_this The object to be used as the value of 'this' + * within callback. + * @return {T|boolean} Value. + * @template T,S + */ +ol.geom.flat.segments.forEach = function(flatCoordinates, offset, end, stride, callback, opt_this) { + var point1 = [flatCoordinates[offset], flatCoordinates[offset + 1]]; + var point2 = []; + var ret; + for (; (offset + stride) < end; offset += stride) { + point2[0] = flatCoordinates[offset + stride]; + point2[1] = flatCoordinates[offset + stride + 1]; + ret = callback.call(opt_this, point1, point2); + if (ret) { + return ret; + } + point1[0] = point2[0]; + point1[1] = point2[1]; + } + return false; +}; + +goog.provide('ol.geom.flat.intersectsextent'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.geom.flat.contains'); +goog.require('ol.geom.flat.segments'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.lineString = function(flatCoordinates, offset, end, stride, extent) { + var coordinatesExtent = ol.extent.extendFlatCoordinates( + ol.extent.createEmpty(), flatCoordinates, offset, end, stride); + if (!ol.extent.intersects(extent, coordinatesExtent)) { + return false; + } + if (ol.extent.containsExtent(extent, coordinatesExtent)) { + return true; + } + if (coordinatesExtent[0] >= extent[0] && + coordinatesExtent[2] <= extent[2]) { + return true; + } + if (coordinatesExtent[1] >= extent[1] && + coordinatesExtent[3] <= extent[3]) { + return true; + } + return ol.geom.flat.segments.forEach(flatCoordinates, offset, end, stride, + /** + * @param {ol.Coordinate} point1 Start point. + * @param {ol.Coordinate} point2 End point. + * @return {boolean} `true` if the segment and the extent intersect, + * `false` otherwise. + */ + function(point1, point2) { + return ol.extent.intersectsSegment(extent, point1, point2); + }); +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.lineStrings = function(flatCoordinates, offset, ends, stride, extent) { + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + if (ol.geom.flat.intersectsextent.lineString( + flatCoordinates, offset, ends[i], stride, extent)) { + return true; + } + offset = ends[i]; + } + return false; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.linearRing = function(flatCoordinates, offset, end, stride, extent) { + if (ol.geom.flat.intersectsextent.lineString( + flatCoordinates, offset, end, stride, extent)) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[0], extent[1])) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[0], extent[3])) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[2], extent[1])) { + return true; + } + if (ol.geom.flat.contains.linearRingContainsXY( + flatCoordinates, offset, end, stride, extent[2], extent[3])) { + return true; + } + return false; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.linearRings = function(flatCoordinates, offset, ends, stride, extent) { + goog.asserts.assert(ends.length > 0, 'ends should not be an empty array'); + if (!ol.geom.flat.intersectsextent.linearRing( + flatCoordinates, offset, ends[0], stride, extent)) { + return false; + } + if (ends.length === 1) { + return true; + } + var i, ii; + for (i = 1, ii = ends.length; i < ii; ++i) { + if (ol.geom.flat.contains.linearRingContainsExtent( + flatCoordinates, ends[i - 1], ends[i], stride, extent)) { + return false; + } + } + return true; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @param {ol.Extent} extent Extent. + * @return {boolean} True if the geometry and the extent intersect. + */ +ol.geom.flat.intersectsextent.linearRingss = function(flatCoordinates, offset, endss, stride, extent) { + goog.asserts.assert(endss.length > 0, 'endss should not be an empty array'); + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + if (ol.geom.flat.intersectsextent.linearRings( + flatCoordinates, offset, ends, stride, extent)) { + return true; + } + offset = ends[ends.length - 1]; + } + return false; +}; + +goog.provide('ol.geom.flat.reverse'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + */ +ol.geom.flat.reverse.coordinates = function(flatCoordinates, offset, end, stride) { + while (offset < end - stride) { + var i; + for (i = 0; i < stride; ++i) { + var tmp = flatCoordinates[offset + i]; + flatCoordinates[offset + i] = flatCoordinates[end - stride + i]; + flatCoordinates[end - stride + i] = tmp; + } + offset += stride; + end -= stride; + } +}; + +goog.provide('ol.geom.flat.orient'); + +goog.require('ol'); +goog.require('ol.geom.flat.reverse'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {boolean} Is clockwise. + */ +ol.geom.flat.orient.linearRingIsClockwise = function(flatCoordinates, offset, end, stride) { + // http://tinyurl.com/clockwise-method + // https://github.com/OSGeo/gdal/blob/trunk/gdal/ogr/ogrlinearring.cpp + var edge = 0; + var x1 = flatCoordinates[end - stride]; + var y1 = flatCoordinates[end - stride + 1]; + for (; offset < end; offset += stride) { + var x2 = flatCoordinates[offset]; + var y2 = flatCoordinates[offset + 1]; + edge += (x2 - x1) * (y2 + y1); + x1 = x2; + y1 = y2; + } + return edge > 0; +}; + + +/** + * Determines if linear rings are oriented. By default, left-hand orientation + * is tested (first ring must be clockwise, remaining rings counter-clockwise). + * To test for right-hand orientation, use the `opt_right` argument. + * + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Array of end indexes. + * @param {number} stride Stride. + * @param {boolean=} opt_right Test for right-hand orientation + * (counter-clockwise exterior ring and clockwise interior rings). + * @return {boolean} Rings are correctly oriented. + */ +ol.geom.flat.orient.linearRingsAreOriented = function(flatCoordinates, offset, ends, stride, opt_right) { + var right = opt_right !== undefined ? opt_right : false; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( + flatCoordinates, offset, end, stride); + if (i === 0) { + if ((right && isClockwise) || (!right && !isClockwise)) { + return false; + } + } else { + if ((right && !isClockwise) || (!right && isClockwise)) { + return false; + } + } + offset = end; + } + return true; +}; + + +/** + * Determines if linear rings are oriented. By default, left-hand orientation + * is tested (first ring must be clockwise, remaining rings counter-clockwise). + * To test for right-hand orientation, use the `opt_right` argument. + * + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Array of array of end indexes. + * @param {number} stride Stride. + * @param {boolean=} opt_right Test for right-hand orientation + * (counter-clockwise exterior ring and clockwise interior rings). + * @return {boolean} Rings are correctly oriented. + */ +ol.geom.flat.orient.linearRingssAreOriented = function(flatCoordinates, offset, endss, stride, opt_right) { + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + if (!ol.geom.flat.orient.linearRingsAreOriented( + flatCoordinates, offset, endss[i], stride, opt_right)) { + return false; + } + } + return true; +}; + + +/** + * Orient coordinates in a flat array of linear rings. By default, rings + * are oriented following the left-hand rule (clockwise for exterior and + * counter-clockwise for interior rings). To orient according to the + * right-hand rule, use the `opt_right` argument. + * + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {boolean=} opt_right Follow the right-hand rule for orientation. + * @return {number} End. + */ +ol.geom.flat.orient.orientLinearRings = function(flatCoordinates, offset, ends, stride, opt_right) { + var right = opt_right !== undefined ? opt_right : false; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + var isClockwise = ol.geom.flat.orient.linearRingIsClockwise( + flatCoordinates, offset, end, stride); + var reverse = i === 0 ? + (right && isClockwise) || (!right && !isClockwise) : + (right && !isClockwise) || (!right && isClockwise); + if (reverse) { + ol.geom.flat.reverse.coordinates(flatCoordinates, offset, end, stride); + } + offset = end; + } + return offset; +}; + + +/** + * Orient coordinates in a flat array of linear rings. By default, rings + * are oriented following the left-hand rule (clockwise for exterior and + * counter-clockwise for interior rings). To orient according to the + * right-hand rule, use the `opt_right` argument. + * + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Array of array of end indexes. + * @param {number} stride Stride. + * @param {boolean=} opt_right Follow the right-hand rule for orientation. + * @return {number} End. + */ +ol.geom.flat.orient.orientLinearRingss = function(flatCoordinates, offset, endss, stride, opt_right) { + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + offset = ol.geom.flat.orient.orientLinearRings( + flatCoordinates, offset, endss[i], stride, opt_right); + } + return offset; +}; + +goog.provide('ol.geom.Polygon'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.area'); +goog.require('ol.geom.flat.closest'); +goog.require('ol.geom.flat.contains'); +goog.require('ol.geom.flat.deflate'); +goog.require('ol.geom.flat.inflate'); +goog.require('ol.geom.flat.interiorpoint'); +goog.require('ol.geom.flat.intersectsextent'); +goog.require('ol.geom.flat.orient'); +goog.require('ol.geom.flat.simplify'); +goog.require('ol.math'); + + +/** + * @classdesc + * Polygon geometry. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.Polygon = function(coordinates, opt_layout) { + + ol.geom.SimpleGeometry.call(this); + + /** + * @type {Array.<number>} + * @private + */ + this.ends_ = []; + + /** + * @private + * @type {number} + */ + this.flatInteriorPointRevision_ = -1; + + /** + * @private + * @type {ol.Coordinate} + */ + this.flatInteriorPoint_ = null; + + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; + + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.orientedRevision_ = -1; + + /** + * @private + * @type {Array.<number>} + */ + this.orientedFlatCoordinates_ = null; + + this.setCoordinates(coordinates, opt_layout); + +}; +ol.inherits(ol.geom.Polygon, ol.geom.SimpleGeometry); + + +/** + * Append the passed linear ring to this polygon. + * @param {ol.geom.LinearRing} linearRing Linear ring. + * @api stable + */ +ol.geom.Polygon.prototype.appendLinearRing = function(linearRing) { + goog.asserts.assert(linearRing.getLayout() == this.layout, + 'layout of linearRing should match layout'); + if (!this.flatCoordinates) { + this.flatCoordinates = linearRing.getFlatCoordinates().slice(); + } else { + ol.array.extend(this.flatCoordinates, linearRing.getFlatCoordinates()); + } + this.ends_.push(this.flatCoordinates.length); + this.changed(); +}; + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.Polygon} Clone. + * @api stable + */ +ol.geom.Polygon.prototype.clone = function() { + var polygon = new ol.geom.Polygon(null); + polygon.setFlatCoordinates( + this.layout, this.flatCoordinates.slice(), this.ends_.slice()); + return polygon; +}; + + +/** + * @inheritDoc + */ +ol.geom.Polygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta( + this.flatCoordinates, 0, this.ends_, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return ol.geom.flat.closest.getsClosestPoint( + this.flatCoordinates, 0, this.ends_, this.stride, + this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); +}; + + +/** + * @inheritDoc + */ +ol.geom.Polygon.prototype.containsXY = function(x, y) { + return ol.geom.flat.contains.linearRingsContainsXY( + this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, x, y); +}; + + +/** + * Return the area of the polygon on projected plane. + * @return {number} Area (on projected plane). + * @api stable + */ +ol.geom.Polygon.prototype.getArea = function() { + return ol.geom.flat.area.linearRings( + this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride); +}; + + +/** + * Get the coordinate array for this geometry. This array has the structure + * of a GeoJSON coordinate array for polygons. + * + * @param {boolean=} opt_right Orient coordinates according to the right-hand + * rule (counter-clockwise for exterior and clockwise for interior rings). + * If `false`, coordinates will be oriented according to the left-hand rule + * (clockwise for exterior and counter-clockwise for interior rings). + * By default, coordinate orientation will depend on how the geometry was + * constructed. + * @return {Array.<Array.<ol.Coordinate>>} Coordinates. + * @api stable + */ +ol.geom.Polygon.prototype.getCoordinates = function(opt_right) { + var flatCoordinates; + if (opt_right !== undefined) { + flatCoordinates = this.getOrientedFlatCoordinates().slice(); + ol.geom.flat.orient.orientLinearRings( + flatCoordinates, 0, this.ends_, this.stride, opt_right); + } else { + flatCoordinates = this.flatCoordinates; + } + + return ol.geom.flat.inflate.coordinatess( + flatCoordinates, 0, this.ends_, this.stride); +}; + + +/** + * @return {Array.<number>} Ends. + */ +ol.geom.Polygon.prototype.getEnds = function() { + return this.ends_; +}; + + +/** + * @return {Array.<number>} Interior point. + */ +ol.geom.Polygon.prototype.getFlatInteriorPoint = function() { + if (this.flatInteriorPointRevision_ != this.getRevision()) { + var flatCenter = ol.extent.getCenter(this.getExtent()); + this.flatInteriorPoint_ = ol.geom.flat.interiorpoint.linearRings( + this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, + flatCenter, 0); + this.flatInteriorPointRevision_ = this.getRevision(); + } + return this.flatInteriorPoint_; +}; + + +/** + * Return an interior point of the polygon. + * @return {ol.geom.Point} Interior point. + * @api stable + */ +ol.geom.Polygon.prototype.getInteriorPoint = function() { + return new ol.geom.Point(this.getFlatInteriorPoint()); +}; + + +/** + * Return the number of rings of the polygon, this includes the exterior + * ring and any interior rings. + * + * @return {number} Number of rings. + * @api + */ +ol.geom.Polygon.prototype.getLinearRingCount = function() { + return this.ends_.length; +}; + + +/** + * Return the Nth linear ring of the polygon geometry. Return `null` if the + * given index is out of range. + * The exterior linear ring is available at index `0` and the interior rings + * at index `1` and beyond. + * + * @param {number} index Index. + * @return {ol.geom.LinearRing} Linear ring. + * @api stable + */ +ol.geom.Polygon.prototype.getLinearRing = function(index) { + goog.asserts.assert(0 <= index && index < this.ends_.length, + 'index should be in between 0 and and length of this.ends_'); + if (index < 0 || this.ends_.length <= index) { + return null; + } + var linearRing = new ol.geom.LinearRing(null); + linearRing.setFlatCoordinates(this.layout, this.flatCoordinates.slice( + index === 0 ? 0 : this.ends_[index - 1], this.ends_[index])); + return linearRing; +}; + + +/** + * Return the linear rings of the polygon. + * @return {Array.<ol.geom.LinearRing>} Linear rings. + * @api stable + */ +ol.geom.Polygon.prototype.getLinearRings = function() { + var layout = this.layout; + var flatCoordinates = this.flatCoordinates; + var ends = this.ends_; + var linearRings = []; + var offset = 0; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + var linearRing = new ol.geom.LinearRing(null); + linearRing.setFlatCoordinates(layout, flatCoordinates.slice(offset, end)); + linearRings.push(linearRing); + offset = end; + } + return linearRings; +}; + + +/** + * @return {Array.<number>} Oriented flat coordinates. + */ +ol.geom.Polygon.prototype.getOrientedFlatCoordinates = function() { + if (this.orientedRevision_ != this.getRevision()) { + var flatCoordinates = this.flatCoordinates; + if (ol.geom.flat.orient.linearRingsAreOriented( + flatCoordinates, 0, this.ends_, this.stride)) { + this.orientedFlatCoordinates_ = flatCoordinates; + } else { + this.orientedFlatCoordinates_ = flatCoordinates.slice(); + this.orientedFlatCoordinates_.length = + ol.geom.flat.orient.orientLinearRings( + this.orientedFlatCoordinates_, 0, this.ends_, this.stride); + } + this.orientedRevision_ = this.getRevision(); + } + return this.orientedFlatCoordinates_; +}; + + +/** + * @inheritDoc + */ +ol.geom.Polygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { + var simplifiedFlatCoordinates = []; + var simplifiedEnds = []; + simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizes( + this.flatCoordinates, 0, this.ends_, this.stride, + Math.sqrt(squaredTolerance), + simplifiedFlatCoordinates, 0, simplifiedEnds); + var simplifiedPolygon = new ol.geom.Polygon(null); + simplifiedPolygon.setFlatCoordinates( + ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds); + return simplifiedPolygon; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.Polygon.prototype.getType = function() { + return ol.geom.GeometryType.POLYGON; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.Polygon.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.linearRings( + this.getOrientedFlatCoordinates(), 0, this.ends_, this.stride, extent); +}; + + +/** + * Set the coordinates of the polygon. + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.Polygon.prototype.setCoordinates = function(coordinates, opt_layout) { + if (!coordinates) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_); + } else { + this.setLayout(opt_layout, coordinates, 2); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + var ends = ol.geom.flat.deflate.coordinatess( + this.flatCoordinates, 0, coordinates, this.stride, this.ends_); + this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {Array.<number>} ends Ends. + */ +ol.geom.Polygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) { + if (!flatCoordinates) { + goog.asserts.assert(ends && ends.length === 0, + 'ends must be an empty array'); + } else if (ends.length === 0) { + goog.asserts.assert(flatCoordinates.length === 0, + 'flatCoordinates should be an empty array'); + } else { + goog.asserts.assert(flatCoordinates.length == ends[ends.length - 1], + 'the length of flatCoordinates should be the last entry of ends'); + } + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.ends_ = ends; + this.changed(); +}; + + +/** + * Create an approximation of a circle on the surface of a sphere. + * @param {ol.Sphere} sphere The sphere. + * @param {ol.Coordinate} center Center (`[lon, lat]` in degrees). + * @param {number} radius The great-circle distance from the center to + * the polygon vertices. + * @param {number=} opt_n Optional number of vertices for the resulting + * polygon. Default is `32`. + * @return {ol.geom.Polygon} The "circular" polygon. + * @api stable + */ +ol.geom.Polygon.circular = function(sphere, center, radius, opt_n) { + var n = opt_n ? opt_n : 32; + /** @type {Array.<number>} */ + var flatCoordinates = []; + var i; + for (i = 0; i < n; ++i) { + ol.array.extend( + flatCoordinates, sphere.offset(center, radius, 2 * Math.PI * i / n)); + } + flatCoordinates.push(flatCoordinates[0], flatCoordinates[1]); + var polygon = new ol.geom.Polygon(null); + polygon.setFlatCoordinates( + ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]); + return polygon; +}; + + +/** + * Create a polygon from an extent. The layout used is `XY`. + * @param {ol.Extent} extent The extent. + * @return {ol.geom.Polygon} The polygon. + * @api + */ +ol.geom.Polygon.fromExtent = function(extent) { + var minX = extent[0]; + var minY = extent[1]; + var maxX = extent[2]; + var maxY = extent[3]; + var flatCoordinates = + [minX, minY, minX, maxY, maxX, maxY, maxX, minY, minX, minY]; + var polygon = new ol.geom.Polygon(null); + polygon.setFlatCoordinates( + ol.geom.GeometryLayout.XY, flatCoordinates, [flatCoordinates.length]); + return polygon; +}; + + +/** + * Create a regular polygon from a circle. + * @param {ol.geom.Circle} circle Circle geometry. + * @param {number=} opt_sides Number of sides of the polygon. Default is 32. + * @param {number=} opt_angle Start angle for the first vertex of the polygon in + * radians. Default is 0. + * @return {ol.geom.Polygon} Polygon geometry. + * @api + */ +ol.geom.Polygon.fromCircle = function(circle, opt_sides, opt_angle) { + var sides = opt_sides ? opt_sides : 32; + var stride = circle.getStride(); + var layout = circle.getLayout(); + var polygon = new ol.geom.Polygon(null, layout); + var arrayLength = stride * (sides + 1); + var flatCoordinates = new Array(arrayLength); + for (var i = 0; i < arrayLength; i++) { + flatCoordinates[i] = 0; + } + var ends = [flatCoordinates.length]; + polygon.setFlatCoordinates(layout, flatCoordinates, ends); + ol.geom.Polygon.makeRegular( + polygon, circle.getCenter(), circle.getRadius(), opt_angle); + return polygon; +}; + + +/** + * Modify the coordinates of a polygon to make it a regular polygon. + * @param {ol.geom.Polygon} polygon Polygon geometry. + * @param {ol.Coordinate} center Center of the regular polygon. + * @param {number} radius Radius of the regular polygon. + * @param {number=} opt_angle Start angle for the first vertex of the polygon in + * radians. Default is 0. + */ +ol.geom.Polygon.makeRegular = function(polygon, center, radius, opt_angle) { + var flatCoordinates = polygon.getFlatCoordinates(); + var layout = polygon.getLayout(); + var stride = polygon.getStride(); + var ends = polygon.getEnds(); + goog.asserts.assert(ends.length === 1, 'only 1 ring is supported'); + var sides = flatCoordinates.length / stride - 1; + var startAngle = opt_angle ? opt_angle : 0; + var angle, offset; + for (var i = 0; i <= sides; ++i) { + offset = i * stride; + angle = startAngle + (ol.math.modulo(i, sides) * 2 * Math.PI / sides); + flatCoordinates[offset] = center[0] + (radius * Math.cos(angle)); + flatCoordinates[offset + 1] = center[1] + (radius * Math.sin(angle)); + } + polygon.setFlatCoordinates(layout, flatCoordinates, ends); +}; + +goog.provide('ol.View'); +goog.provide('ol.ViewHint'); +goog.provide('ol.ViewProperty'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.CenterConstraint'); +goog.require('ol.Constraints'); +goog.require('ol.Object'); +goog.require('ol.ResolutionConstraint'); +goog.require('ol.RotationConstraint'); +goog.require('ol.coordinate'); +goog.require('ol.extent'); +goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.proj'); +goog.require('ol.proj.METERS_PER_UNIT'); +goog.require('ol.proj.Projection'); +goog.require('ol.proj.Units'); + + +/** + * @enum {string} + */ +ol.ViewProperty = { + CENTER: 'center', + RESOLUTION: 'resolution', + ROTATION: 'rotation' +}; + + +/** + * @enum {number} + */ +ol.ViewHint = { + ANIMATING: 0, + INTERACTING: 1 +}; + + +/** + * @classdesc + * An ol.View object represents a simple 2D view of the map. + * + * This is the object to act upon to change the center, resolution, + * and rotation of the map. + * + * ### The view states + * + * An `ol.View` is determined by three states: `center`, `resolution`, + * and `rotation`. Each state has a corresponding getter and setter, e.g. + * `getCenter` and `setCenter` for the `center` state. + * + * An `ol.View` has a `projection`. The projection determines the + * coordinate system of the center, and its units determine the units of the + * resolution (projection units per pixel). The default projection is + * Spherical Mercator (EPSG:3857). + * + * ### The constraints + * + * `setCenter`, `setResolution` and `setRotation` can be used to change the + * states of the view. Any value can be passed to the setters. And the value + * that is passed to a setter will effectively be the value set in the view, + * and returned by the corresponding getter. + * + * But an `ol.View` object also has a *resolution constraint*, a + * *rotation constraint* and a *center constraint*. + * + * As said above, no constraints are applied when the setters are used to set + * new states for the view. Applying constraints is done explicitly through + * the use of the `constrain*` functions (`constrainResolution` and + * `constrainRotation` and `constrainCenter`). + * + * The main users of the constraints are the interactions and the + * controls. For example, double-clicking on the map changes the view to + * the "next" resolution. And releasing the fingers after pinch-zooming + * snaps to the closest resolution (with an animation). + * + * The *resolution constraint* snaps to specific resolutions. It is + * determined by the following options: `resolutions`, `maxResolution`, + * `maxZoom`, and `zoomFactor`. If `resolutions` is set, the other three + * options are ignored. See documentation for each option for more + * information. + * + * The *rotation constraint* snaps to specific angles. It is determined + * by the following options: `enableRotation` and `constrainRotation`. + * By default the rotation value is snapped to zero when approaching the + * horizontal. + * + * The *center constraint* is determined by the `extent` option. By + * default the center is not constrained at all. + * + * @constructor + * @extends {ol.Object} + * @param {olx.ViewOptions=} opt_options View options. + * @api stable + */ +ol.View = function(opt_options) { + ol.Object.call(this); + var options = opt_options || {}; + + /** + * @private + * @type {Array.<number>} + */ + this.hints_ = [0, 0]; + + /** + * @type {Object.<string, *>} + */ + var properties = {}; + properties[ol.ViewProperty.CENTER] = options.center !== undefined ? + options.center : null; + + /** + * @private + * @const + * @type {ol.proj.Projection} + */ + this.projection_ = ol.proj.createProjection(options.projection, 'EPSG:3857'); + + var resolutionConstraintInfo = ol.View.createResolutionConstraint_( + options); + + /** + * @private + * @type {number} + */ + this.maxResolution_ = resolutionConstraintInfo.maxResolution; + + /** + * @private + * @type {number} + */ + this.minResolution_ = resolutionConstraintInfo.minResolution; + + /** + * @private + * @type {Array.<number>|undefined} + */ + this.resolutions_ = options.resolutions; + + /** + * @private + * @type {number} + */ + this.minZoom_ = resolutionConstraintInfo.minZoom; + + var centerConstraint = ol.View.createCenterConstraint_(options); + var resolutionConstraint = resolutionConstraintInfo.constraint; + var rotationConstraint = ol.View.createRotationConstraint_(options); + + /** + * @private + * @type {ol.Constraints} + */ + this.constraints_ = new ol.Constraints( + centerConstraint, resolutionConstraint, rotationConstraint); + + if (options.resolution !== undefined) { + properties[ol.ViewProperty.RESOLUTION] = options.resolution; + } else if (options.zoom !== undefined) { + properties[ol.ViewProperty.RESOLUTION] = this.constrainResolution( + this.maxResolution_, options.zoom - this.minZoom_); + } + properties[ol.ViewProperty.ROTATION] = + options.rotation !== undefined ? options.rotation : 0; + this.setProperties(properties); +}; +ol.inherits(ol.View, ol.Object); + + +/** + * @param {number} rotation Target rotation. + * @param {ol.Coordinate} anchor Rotation anchor. + * @return {ol.Coordinate|undefined} Center for rotation and anchor. + */ +ol.View.prototype.calculateCenterRotate = function(rotation, anchor) { + var center; + var currentCenter = this.getCenter(); + if (currentCenter !== undefined) { + center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]]; + ol.coordinate.rotate(center, rotation - this.getRotation()); + ol.coordinate.add(center, anchor); + } + return center; +}; + + +/** + * @param {number} resolution Target resolution. + * @param {ol.Coordinate} anchor Zoom anchor. + * @return {ol.Coordinate|undefined} Center for resolution and anchor. + */ +ol.View.prototype.calculateCenterZoom = function(resolution, anchor) { + var center; + var currentCenter = this.getCenter(); + var currentResolution = this.getResolution(); + if (currentCenter !== undefined && currentResolution !== undefined) { + var x = anchor[0] - + resolution * (anchor[0] - currentCenter[0]) / currentResolution; + var y = anchor[1] - + resolution * (anchor[1] - currentCenter[1]) / currentResolution; + center = [x, y]; + } + return center; +}; + + +/** + * Get the constrained center of this view. + * @param {ol.Coordinate|undefined} center Center. + * @return {ol.Coordinate|undefined} Constrained center. + * @api + */ +ol.View.prototype.constrainCenter = function(center) { + return this.constraints_.center(center); +}; + + +/** + * Get the constrained resolution of this view. + * @param {number|undefined} resolution Resolution. + * @param {number=} opt_delta Delta. Default is `0`. + * @param {number=} opt_direction Direction. Default is `0`. + * @return {number|undefined} Constrained resolution. + * @api + */ +ol.View.prototype.constrainResolution = function( + resolution, opt_delta, opt_direction) { + var delta = opt_delta || 0; + var direction = opt_direction || 0; + return this.constraints_.resolution(resolution, delta, direction); +}; + + +/** + * Get the constrained rotation of this view. + * @param {number|undefined} rotation Rotation. + * @param {number=} opt_delta Delta. Default is `0`. + * @return {number|undefined} Constrained rotation. + * @api + */ +ol.View.prototype.constrainRotation = function(rotation, opt_delta) { + var delta = opt_delta || 0; + return this.constraints_.rotation(rotation, delta); +}; + + +/** + * Get the view center. + * @return {ol.Coordinate|undefined} The center of the view. + * @observable + * @api stable + */ +ol.View.prototype.getCenter = function() { + return /** @type {ol.Coordinate|undefined} */ ( + this.get(ol.ViewProperty.CENTER)); +}; + + +/** + * @param {Array.<number>=} opt_hints Destination array. + * @return {Array.<number>} Hint. + */ +ol.View.prototype.getHints = function(opt_hints) { + if (opt_hints !== undefined) { + opt_hints[0] = this.hints_[0]; + opt_hints[1] = this.hints_[1]; + return opt_hints; + } else { + return this.hints_.slice(); + } +}; + + +/** + * Calculate the extent for the current view state and the passed size. + * The size is the pixel dimensions of the box into which the calculated extent + * should fit. In most cases you want to get the extent of the entire map, + * that is `map.getSize()`. + * @param {ol.Size} size Box pixel size. + * @return {ol.Extent} Extent. + * @api stable + */ +ol.View.prototype.calculateExtent = function(size) { + var center = this.getCenter(); + goog.asserts.assert(center, 'The view center is not defined'); + var resolution = this.getResolution(); + goog.asserts.assert(resolution !== undefined, + 'The view resolution is not defined'); + var rotation = this.getRotation(); + goog.asserts.assert(rotation !== undefined, + 'The view rotation is not defined'); + + return ol.extent.getForViewAndSize(center, resolution, rotation, size); +}; + + +/** + * Get the maximum resolution of the view. + * @return {number} The maximum resolution of the view. + * @api + */ +ol.View.prototype.getMaxResolution = function() { + return this.maxResolution_; +}; + + +/** + * Get the minimum resolution of the view. + * @return {number} The minimum resolution of the view. + * @api + */ +ol.View.prototype.getMinResolution = function() { + return this.minResolution_; +}; + + +/** + * Get the view projection. + * @return {ol.proj.Projection} The projection of the view. + * @api stable + */ +ol.View.prototype.getProjection = function() { + return this.projection_; +}; + + +/** + * Get the view resolution. + * @return {number|undefined} The resolution of the view. + * @observable + * @api stable + */ +ol.View.prototype.getResolution = function() { + return /** @type {number|undefined} */ ( + this.get(ol.ViewProperty.RESOLUTION)); +}; + + +/** + * Get the resolutions for the view. This returns the array of resolutions + * passed to the constructor of the {ol.View}, or undefined if none were given. + * @return {Array.<number>|undefined} The resolutions of the view. + * @api stable + */ +ol.View.prototype.getResolutions = function() { + return this.resolutions_; +}; + + +/** + * Get the resolution for a provided extent (in map units) and size (in pixels). + * @param {ol.Extent} extent Extent. + * @param {ol.Size} size Box pixel size. + * @return {number} The resolution at which the provided extent will render at + * the given size. + */ +ol.View.prototype.getResolutionForExtent = function(extent, size) { + var xResolution = ol.extent.getWidth(extent) / size[0]; + var yResolution = ol.extent.getHeight(extent) / size[1]; + return Math.max(xResolution, yResolution); +}; + + +/** + * Return a function that returns a value between 0 and 1 for a + * resolution. Exponential scaling is assumed. + * @param {number=} opt_power Power. + * @return {function(number): number} Resolution for value function. + */ +ol.View.prototype.getResolutionForValueFunction = function(opt_power) { + var power = opt_power || 2; + var maxResolution = this.maxResolution_; + var minResolution = this.minResolution_; + var max = Math.log(maxResolution / minResolution) / Math.log(power); + return ( + /** + * @param {number} value Value. + * @return {number} Resolution. + */ + function(value) { + var resolution = maxResolution / Math.pow(power, value * max); + goog.asserts.assert(resolution >= minResolution && + resolution <= maxResolution, + 'calculated resolution outside allowed bounds (%s <= %s <= %s)', + minResolution, resolution, maxResolution); + return resolution; + }); +}; + + +/** + * Get the view rotation. + * @return {number} The rotation of the view in radians. + * @observable + * @api stable + */ +ol.View.prototype.getRotation = function() { + return /** @type {number} */ (this.get(ol.ViewProperty.ROTATION)); +}; + + +/** + * Return a function that returns a resolution for a value between + * 0 and 1. Exponential scaling is assumed. + * @param {number=} opt_power Power. + * @return {function(number): number} Value for resolution function. + */ +ol.View.prototype.getValueForResolutionFunction = function(opt_power) { + var power = opt_power || 2; + var maxResolution = this.maxResolution_; + var minResolution = this.minResolution_; + var max = Math.log(maxResolution / minResolution) / Math.log(power); + return ( + /** + * @param {number} resolution Resolution. + * @return {number} Value. + */ + function(resolution) { + var value = + (Math.log(maxResolution / resolution) / Math.log(power)) / max; + goog.asserts.assert(value >= 0 && value <= 1, + 'calculated value (%s) ouside allowed range (0-1)', value); + return value; + }); +}; + + +/** + * @return {olx.ViewState} View state. + */ +ol.View.prototype.getState = function() { + goog.asserts.assert(this.isDef(), + 'the view was not defined (had no center and/or resolution)'); + var center = /** @type {ol.Coordinate} */ (this.getCenter()); + var projection = this.getProjection(); + var resolution = /** @type {number} */ (this.getResolution()); + var rotation = this.getRotation(); + return /** @type {olx.ViewState} */ ({ + // Snap center to closest pixel + center: [ + Math.round(center[0] / resolution) * resolution, + Math.round(center[1] / resolution) * resolution + ], + projection: projection !== undefined ? projection : null, + resolution: resolution, + rotation: rotation + }); +}; + + +/** + * Get the current zoom level. Return undefined if the current + * resolution is undefined or not a "constrained resolution". + * @return {number|undefined} Zoom. + * @api stable + */ +ol.View.prototype.getZoom = function() { + var offset; + var resolution = this.getResolution(); + + if (resolution !== undefined) { + var res, z = 0; + do { + res = this.constrainResolution(this.maxResolution_, z); + if (res == resolution) { + offset = z; + break; + } + ++z; + } while (res > this.minResolution_); + } + + return offset !== undefined ? this.minZoom_ + offset : offset; +}; + + +/** + * Fit the given geometry or extent based on the given map size and border. + * The size is pixel dimensions of the box to fit the extent into. + * In most cases you will want to use the map size, that is `map.getSize()`. + * Takes care of the map angle. + * @param {ol.geom.SimpleGeometry|ol.Extent} geometry Geometry. + * @param {ol.Size} size Box pixel size. + * @param {olx.view.FitOptions=} opt_options Options. + * @api + */ +ol.View.prototype.fit = function(geometry, size, opt_options) { + if (!(geometry instanceof ol.geom.SimpleGeometry)) { + goog.asserts.assert(Array.isArray(geometry), + 'invalid extent or geometry'); + goog.asserts.assert(!ol.extent.isEmpty(geometry), + 'cannot fit empty extent'); + geometry = ol.geom.Polygon.fromExtent(geometry); + } + + var options = opt_options || {}; + + var padding = options.padding !== undefined ? options.padding : [0, 0, 0, 0]; + var constrainResolution = options.constrainResolution !== undefined ? + options.constrainResolution : true; + var nearest = options.nearest !== undefined ? options.nearest : false; + var minResolution; + if (options.minResolution !== undefined) { + minResolution = options.minResolution; + } else if (options.maxZoom !== undefined) { + minResolution = this.constrainResolution( + this.maxResolution_, options.maxZoom - this.minZoom_, 0); + } else { + minResolution = 0; + } + var coords = geometry.getFlatCoordinates(); + + // calculate rotated extent + var rotation = this.getRotation(); + goog.asserts.assert(rotation !== undefined, 'rotation was not defined'); + var cosAngle = Math.cos(-rotation); + var sinAngle = Math.sin(-rotation); + var minRotX = +Infinity; + var minRotY = +Infinity; + var maxRotX = -Infinity; + var maxRotY = -Infinity; + var stride = geometry.getStride(); + for (var i = 0, ii = coords.length; i < ii; i += stride) { + var rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle; + var rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle; + minRotX = Math.min(minRotX, rotX); + minRotY = Math.min(minRotY, rotY); + maxRotX = Math.max(maxRotX, rotX); + maxRotY = Math.max(maxRotY, rotY); + } + + // calculate resolution + var resolution = this.getResolutionForExtent( + [minRotX, minRotY, maxRotX, maxRotY], + [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]]); + resolution = isNaN(resolution) ? minResolution : + Math.max(resolution, minResolution); + if (constrainResolution) { + var constrainedResolution = this.constrainResolution(resolution, 0, 0); + if (!nearest && constrainedResolution < resolution) { + constrainedResolution = this.constrainResolution( + constrainedResolution, -1, 0); + } + resolution = constrainedResolution; + } + this.setResolution(resolution); + + // calculate center + sinAngle = -sinAngle; // go back to original rotation + var centerRotX = (minRotX + maxRotX) / 2; + var centerRotY = (minRotY + maxRotY) / 2; + centerRotX += (padding[1] - padding[3]) / 2 * resolution; + centerRotY += (padding[0] - padding[2]) / 2 * resolution; + var centerX = centerRotX * cosAngle - centerRotY * sinAngle; + var centerY = centerRotY * cosAngle + centerRotX * sinAngle; + + this.setCenter([centerX, centerY]); +}; + + +/** + * Center on coordinate and view position. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.Size} size Box pixel size. + * @param {ol.Pixel} position Position on the view to center on. + * @api + */ +ol.View.prototype.centerOn = function(coordinate, size, position) { + // calculate rotated position + var rotation = this.getRotation(); + var cosAngle = Math.cos(-rotation); + var sinAngle = Math.sin(-rotation); + var rotX = coordinate[0] * cosAngle - coordinate[1] * sinAngle; + var rotY = coordinate[1] * cosAngle + coordinate[0] * sinAngle; + var resolution = this.getResolution(); + rotX += (size[0] / 2 - position[0]) * resolution; + rotY += (position[1] - size[1] / 2) * resolution; + + // go back to original angle + sinAngle = -sinAngle; // go back to original rotation + var centerX = rotX * cosAngle - rotY * sinAngle; + var centerY = rotY * cosAngle + rotX * sinAngle; + + this.setCenter([centerX, centerY]); +}; + + +/** + * @return {boolean} Is defined. + */ +ol.View.prototype.isDef = function() { + return !!this.getCenter() && this.getResolution() !== undefined; +}; + + +/** + * Rotate the view around a given coordinate. + * @param {number} rotation New rotation value for the view. + * @param {ol.Coordinate=} opt_anchor The rotation center. + * @api stable + */ +ol.View.prototype.rotate = function(rotation, opt_anchor) { + if (opt_anchor !== undefined) { + var center = this.calculateCenterRotate(rotation, opt_anchor); + this.setCenter(center); + } + this.setRotation(rotation); +}; + + +/** + * Set the center of the current view. + * @param {ol.Coordinate|undefined} center The center of the view. + * @observable + * @api stable + */ +ol.View.prototype.setCenter = function(center) { + this.set(ol.ViewProperty.CENTER, center); +}; + + +/** + * @param {ol.ViewHint} hint Hint. + * @param {number} delta Delta. + * @return {number} New value. + */ +ol.View.prototype.setHint = function(hint, delta) { + goog.asserts.assert(0 <= hint && hint < this.hints_.length, + 'illegal hint (%s), must be between 0 and %s', hint, this.hints_.length); + this.hints_[hint] += delta; + goog.asserts.assert(this.hints_[hint] >= 0, + 'Hint at %s must be positive, was %s', hint, this.hints_[hint]); + return this.hints_[hint]; +}; + + +/** + * Set the resolution for this view. + * @param {number|undefined} resolution The resolution of the view. + * @observable + * @api stable + */ +ol.View.prototype.setResolution = function(resolution) { + this.set(ol.ViewProperty.RESOLUTION, resolution); +}; + + +/** + * Set the rotation for this view. + * @param {number} rotation The rotation of the view in radians. + * @observable + * @api stable + */ +ol.View.prototype.setRotation = function(rotation) { + this.set(ol.ViewProperty.ROTATION, rotation); +}; + + +/** + * Zoom to a specific zoom level. + * @param {number} zoom Zoom level. + * @api stable + */ +ol.View.prototype.setZoom = function(zoom) { + var resolution = this.constrainResolution( + this.maxResolution_, zoom - this.minZoom_, 0); + this.setResolution(resolution); +}; + + +/** + * @param {olx.ViewOptions} options View options. + * @private + * @return {ol.CenterConstraintType} The constraint. + */ +ol.View.createCenterConstraint_ = function(options) { + if (options.extent !== undefined) { + return ol.CenterConstraint.createExtent(options.extent); + } else { + return ol.CenterConstraint.none; + } +}; + + +/** + * @private + * @param {olx.ViewOptions} options View options. + * @return {{constraint: ol.ResolutionConstraintType, maxResolution: number, + * minResolution: number}} The constraint. + */ +ol.View.createResolutionConstraint_ = function(options) { + var resolutionConstraint; + var maxResolution; + var minResolution; + + // TODO: move these to be ol constants + // see https://github.com/openlayers/ol3/issues/2076 + var defaultMaxZoom = 28; + var defaultZoomFactor = 2; + + var minZoom = options.minZoom !== undefined ? + options.minZoom : ol.DEFAULT_MIN_ZOOM; + + var maxZoom = options.maxZoom !== undefined ? + options.maxZoom : defaultMaxZoom; + + var zoomFactor = options.zoomFactor !== undefined ? + options.zoomFactor : defaultZoomFactor; + + if (options.resolutions !== undefined) { + var resolutions = options.resolutions; + maxResolution = resolutions[0]; + minResolution = resolutions[resolutions.length - 1]; + resolutionConstraint = ol.ResolutionConstraint.createSnapToResolutions( + resolutions); + } else { + // calculate the default min and max resolution + var projection = ol.proj.createProjection(options.projection, 'EPSG:3857'); + var extent = projection.getExtent(); + var size = !extent ? + // use an extent that can fit the whole world if need be + 360 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] / + projection.getMetersPerUnit() : + Math.max(ol.extent.getWidth(extent), ol.extent.getHeight(extent)); + + var defaultMaxResolution = size / ol.DEFAULT_TILE_SIZE / Math.pow( + defaultZoomFactor, ol.DEFAULT_MIN_ZOOM); + + var defaultMinResolution = defaultMaxResolution / Math.pow( + defaultZoomFactor, defaultMaxZoom - ol.DEFAULT_MIN_ZOOM); + + // user provided maxResolution takes precedence + maxResolution = options.maxResolution; + if (maxResolution !== undefined) { + minZoom = 0; + } else { + maxResolution = defaultMaxResolution / Math.pow(zoomFactor, minZoom); + } + + // user provided minResolution takes precedence + minResolution = options.minResolution; + if (minResolution === undefined) { + if (options.maxZoom !== undefined) { + if (options.maxResolution !== undefined) { + minResolution = maxResolution / Math.pow(zoomFactor, maxZoom); + } else { + minResolution = defaultMaxResolution / Math.pow(zoomFactor, maxZoom); + } + } else { + minResolution = defaultMinResolution; + } + } + + // given discrete zoom levels, minResolution may be different than provided + maxZoom = minZoom + Math.floor( + Math.log(maxResolution / minResolution) / Math.log(zoomFactor)); + minResolution = maxResolution / Math.pow(zoomFactor, maxZoom - minZoom); + + resolutionConstraint = ol.ResolutionConstraint.createSnapToPower( + zoomFactor, maxResolution, maxZoom - minZoom); + } + return {constraint: resolutionConstraint, maxResolution: maxResolution, + minResolution: minResolution, minZoom: minZoom}; +}; + + +/** + * @private + * @param {olx.ViewOptions} options View options. + * @return {ol.RotationConstraintType} Rotation constraint. + */ +ol.View.createRotationConstraint_ = function(options) { + var enableRotation = options.enableRotation !== undefined ? + options.enableRotation : true; + if (enableRotation) { + var constrainRotation = options.constrainRotation; + if (constrainRotation === undefined || constrainRotation === true) { + return ol.RotationConstraint.createSnapToZero(); + } else if (constrainRotation === false) { + return ol.RotationConstraint.none; + } else if (goog.isNumber(constrainRotation)) { + return ol.RotationConstraint.createSnapToN(constrainRotation); + } else { + goog.asserts.fail( + 'illegal option for constrainRotation (%s)', constrainRotation); + return ol.RotationConstraint.none; + } + } else { + return ol.RotationConstraint.disable; + } +}; + +goog.provide('ol.easing'); + + +/** + * Start slow and speed up. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + * @api + */ +ol.easing.easeIn = function(t) { + return Math.pow(t, 3); +}; + + +/** + * Start fast and slow down. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + * @api + */ +ol.easing.easeOut = function(t) { + return 1 - ol.easing.easeIn(1 - t); +}; + + +/** + * Start slow, speed up, and then slow down again. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + * @api + */ +ol.easing.inAndOut = function(t) { + return 3 * t * t - 2 * t * t * t; +}; + + +/** + * Maintain a constant speed over time. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + * @api + */ +ol.easing.linear = function(t) { + return t; +}; + + +/** + * Start slow, speed up, and at the very end slow down again. This has the + * same general behavior as {@link ol.easing.inAndOut}, but the final slowdown + * is delayed. + * @param {number} t Input between 0 and 1. + * @return {number} Output between 0 and 1. + * @api + */ +ol.easing.upAndDown = function(t) { + if (t < 0.5) { + return ol.easing.inAndOut(2 * t); + } else { + return 1 - ol.easing.inAndOut(2 * (t - 0.5)); + } +}; + +goog.provide('ol.animation'); + +goog.require('ol'); +goog.require('ol.ViewHint'); +goog.require('ol.coordinate'); +goog.require('ol.easing'); + + +/** + * Generate an animated transition that will "bounce" the resolution as it + * approaches the final value. + * @param {olx.animation.BounceOptions} options Bounce options. + * @return {ol.PreRenderFunction} Pre-render function. + * @api + */ +ol.animation.bounce = function(options) { + var resolution = options.resolution; + var start = options.start ? options.start : Date.now(); + var duration = options.duration !== undefined ? options.duration : 1000; + var easing = options.easing ? + options.easing : ol.easing.upAndDown; + return ( + /** + * @param {ol.Map} map Map. + * @param {?olx.FrameState} frameState Frame state. + * @return {boolean} Run this function in the next frame. + */ + function(map, frameState) { + if (frameState.time < start) { + frameState.animate = true; + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else if (frameState.time < start + duration) { + var delta = easing((frameState.time - start) / duration); + var deltaResolution = resolution - frameState.viewState.resolution; + frameState.animate = true; + frameState.viewState.resolution += delta * deltaResolution; + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else { + return false; + } + }); +}; + + +/** + * Generate an animated transition while updating the view center. + * @param {olx.animation.PanOptions} options Pan options. + * @return {ol.PreRenderFunction} Pre-render function. + * @api + */ +ol.animation.pan = function(options) { + var source = options.source; + var start = options.start ? options.start : Date.now(); + var sourceX = source[0]; + var sourceY = source[1]; + var duration = options.duration !== undefined ? options.duration : 1000; + var easing = options.easing ? + options.easing : ol.easing.inAndOut; + return ( + /** + * @param {ol.Map} map Map. + * @param {?olx.FrameState} frameState Frame state. + * @return {boolean} Run this function in the next frame. + */ + function(map, frameState) { + if (frameState.time < start) { + frameState.animate = true; + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else if (frameState.time < start + duration) { + var delta = 1 - easing((frameState.time - start) / duration); + var deltaX = sourceX - frameState.viewState.center[0]; + var deltaY = sourceY - frameState.viewState.center[1]; + frameState.animate = true; + frameState.viewState.center[0] += delta * deltaX; + frameState.viewState.center[1] += delta * deltaY; + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else { + return false; + } + }); +}; + + +/** + * Generate an animated transition while updating the view rotation. + * @param {olx.animation.RotateOptions} options Rotate options. + * @return {ol.PreRenderFunction} Pre-render function. + * @api + */ +ol.animation.rotate = function(options) { + var sourceRotation = options.rotation ? options.rotation : 0; + var start = options.start ? options.start : Date.now(); + var duration = options.duration !== undefined ? options.duration : 1000; + var easing = options.easing ? + options.easing : ol.easing.inAndOut; + var anchor = options.anchor ? + options.anchor : null; + + return ( + /** + * @param {ol.Map} map Map. + * @param {?olx.FrameState} frameState Frame state. + * @return {boolean} Run this function in the next frame. + */ + function(map, frameState) { + if (frameState.time < start) { + frameState.animate = true; + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else if (frameState.time < start + duration) { + var delta = 1 - easing((frameState.time - start) / duration); + var deltaRotation = + (sourceRotation - frameState.viewState.rotation) * delta; + frameState.animate = true; + frameState.viewState.rotation += deltaRotation; + if (anchor) { + var center = frameState.viewState.center; + ol.coordinate.sub(center, anchor); + ol.coordinate.rotate(center, deltaRotation); + ol.coordinate.add(center, anchor); + } + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else { + return false; + } + }); +}; + + +/** + * Generate an animated transition while updating the view resolution. + * @param {olx.animation.ZoomOptions} options Zoom options. + * @return {ol.PreRenderFunction} Pre-render function. + * @api + */ +ol.animation.zoom = function(options) { + var sourceResolution = options.resolution; + var start = options.start ? options.start : Date.now(); + var duration = options.duration !== undefined ? options.duration : 1000; + var easing = options.easing ? + options.easing : ol.easing.inAndOut; + return ( + /** + * @param {ol.Map} map Map. + * @param {?olx.FrameState} frameState Frame state. + * @return {boolean} Run this function in the next frame. + */ + function(map, frameState) { + if (frameState.time < start) { + frameState.animate = true; + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else if (frameState.time < start + duration) { + var delta = 1 - easing((frameState.time - start) / duration); + var deltaResolution = + sourceResolution - frameState.viewState.resolution; + frameState.animate = true; + frameState.viewState.resolution += delta * deltaResolution; + frameState.viewHints[ol.ViewHint.ANIMATING] += 1; + return true; + } else { + return false; + } + }); +}; + +goog.provide('ol.TileRange'); + +goog.require('goog.asserts'); + + +/** + * A representation of a contiguous block of tiles. A tile range is specified + * by its min/max tile coordinates and is inclusive of coordinates. + * + * @constructor + * @param {number} minX Minimum X. + * @param {number} maxX Maximum X. + * @param {number} minY Minimum Y. + * @param {number} maxY Maximum Y. + * @struct + */ +ol.TileRange = function(minX, maxX, minY, maxY) { + + /** + * @type {number} + */ + this.minX = minX; + + /** + * @type {number} + */ + this.maxX = maxX; + + /** + * @type {number} + */ + this.minY = minY; + + /** + * @type {number} + */ + this.maxY = maxY; + +}; + + +/** + * @param {...ol.TileCoord} var_args Tile coordinates. + * @return {!ol.TileRange} Bounding tile box. + */ +ol.TileRange.boundingTileRange = function(var_args) { + var tileCoord0 = /** @type {ol.TileCoord} */ (arguments[0]); + var tileCoord0Z = tileCoord0[0]; + var tileCoord0X = tileCoord0[1]; + var tileCoord0Y = tileCoord0[2]; + var tileRange = new ol.TileRange(tileCoord0X, tileCoord0X, + tileCoord0Y, tileCoord0Y); + var i, ii, tileCoord, tileCoordX, tileCoordY, tileCoordZ; + for (i = 1, ii = arguments.length; i < ii; ++i) { + tileCoord = /** @type {ol.TileCoord} */ (arguments[i]); + tileCoordZ = tileCoord[0]; + tileCoordX = tileCoord[1]; + tileCoordY = tileCoord[2]; + goog.asserts.assert(tileCoordZ == tileCoord0Z, + 'passed tilecoords all have the same Z-value'); + tileRange.minX = Math.min(tileRange.minX, tileCoordX); + tileRange.maxX = Math.max(tileRange.maxX, tileCoordX); + tileRange.minY = Math.min(tileRange.minY, tileCoordY); + tileRange.maxY = Math.max(tileRange.maxY, tileCoordY); + } + return tileRange; +}; + + +/** + * @param {number} minX Minimum X. + * @param {number} maxX Maximum X. + * @param {number} minY Minimum Y. + * @param {number} maxY Maximum Y. + * @param {ol.TileRange|undefined} tileRange TileRange. + * @return {ol.TileRange} Tile range. + */ +ol.TileRange.createOrUpdate = function(minX, maxX, minY, maxY, tileRange) { + if (tileRange !== undefined) { + tileRange.minX = minX; + tileRange.maxX = maxX; + tileRange.minY = minY; + tileRange.maxY = maxY; + return tileRange; + } else { + return new ol.TileRange(minX, maxX, minY, maxY); + } +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @return {boolean} Contains tile coordinate. + */ +ol.TileRange.prototype.contains = function(tileCoord) { + return this.containsXY(tileCoord[1], tileCoord[2]); +}; + + +/** + * @param {ol.TileRange} tileRange Tile range. + * @return {boolean} Contains. + */ +ol.TileRange.prototype.containsTileRange = function(tileRange) { + return this.minX <= tileRange.minX && tileRange.maxX <= this.maxX && + this.minY <= tileRange.minY && tileRange.maxY <= this.maxY; +}; + + +/** + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @return {boolean} Contains coordinate. + */ +ol.TileRange.prototype.containsXY = function(x, y) { + return this.minX <= x && x <= this.maxX && this.minY <= y && y <= this.maxY; +}; + + +/** + * @param {ol.TileRange} tileRange Tile range. + * @return {boolean} Equals. + */ +ol.TileRange.prototype.equals = function(tileRange) { + return this.minX == tileRange.minX && this.minY == tileRange.minY && + this.maxX == tileRange.maxX && this.maxY == tileRange.maxY; +}; + + +/** + * @param {ol.TileRange} tileRange Tile range. + */ +ol.TileRange.prototype.extend = function(tileRange) { + if (tileRange.minX < this.minX) { + this.minX = tileRange.minX; + } + if (tileRange.maxX > this.maxX) { + this.maxX = tileRange.maxX; + } + if (tileRange.minY < this.minY) { + this.minY = tileRange.minY; + } + if (tileRange.maxY > this.maxY) { + this.maxY = tileRange.maxY; + } +}; + + +/** + * @return {number} Height. + */ +ol.TileRange.prototype.getHeight = function() { + return this.maxY - this.minY + 1; +}; + + +/** + * @return {ol.Size} Size. + */ +ol.TileRange.prototype.getSize = function() { + return [this.getWidth(), this.getHeight()]; +}; + + +/** + * @return {number} Width. + */ +ol.TileRange.prototype.getWidth = function() { + return this.maxX - this.minX + 1; +}; + + +/** + * @param {ol.TileRange} tileRange Tile range. + * @return {boolean} Intersects. + */ +ol.TileRange.prototype.intersects = function(tileRange) { + return this.minX <= tileRange.maxX && + this.maxX >= tileRange.minX && + this.minY <= tileRange.maxY && + this.maxY >= tileRange.minY; +}; + +goog.provide('ol.Attribution'); + +goog.require('ol.TileRange'); +goog.require('ol.math'); + + +/** + * @classdesc + * An attribution for a layer source. + * + * Example: + * + * source: new ol.source.OSM({ + * attributions: [ + * new ol.Attribution({ + * html: 'All maps © ' + + * '<a href="http://www.opencyclemap.org/">OpenCycleMap</a>' + * }), + * ol.source.OSM.ATTRIBUTION + * ], + * .. + * + * @constructor + * @param {olx.AttributionOptions} options Attribution options. + * @struct + * @api stable + */ +ol.Attribution = function(options) { + + /** + * @private + * @type {string} + */ + this.html_ = options.html; + + /** + * @private + * @type {Object.<string, Array.<ol.TileRange>>} + */ + this.tileRanges_ = options.tileRanges ? options.tileRanges : null; + +}; + + +/** + * Get the attribution markup. + * @return {string} The attribution HTML. + * @api stable + */ +ol.Attribution.prototype.getHTML = function() { + return this.html_; +}; + + +/** + * @param {Object.<string, ol.TileRange>} tileRanges Tile ranges. + * @param {!ol.tilegrid.TileGrid} tileGrid Tile grid. + * @param {!ol.proj.Projection} projection Projection. + * @return {boolean} Intersects any tile range. + */ +ol.Attribution.prototype.intersectsAnyTileRange = function(tileRanges, tileGrid, projection) { + if (!this.tileRanges_) { + return true; + } + var i, ii, tileRange, zKey; + for (zKey in tileRanges) { + if (!(zKey in this.tileRanges_)) { + continue; + } + tileRange = tileRanges[zKey]; + var testTileRange; + for (i = 0, ii = this.tileRanges_[zKey].length; i < ii; ++i) { + testTileRange = this.tileRanges_[zKey][i]; + if (testTileRange.intersects(tileRange)) { + return true; + } + var extentTileRange = tileGrid.getTileRangeForExtentAndZ( + ol.tilegrid.extentFromProjection(projection), parseInt(zKey, 10)); + var width = extentTileRange.getWidth(); + if (tileRange.minX < extentTileRange.minX || + tileRange.maxX > extentTileRange.maxX) { + if (testTileRange.intersects(new ol.TileRange( + ol.math.modulo(tileRange.minX, width), + ol.math.modulo(tileRange.maxX, width), + tileRange.minY, tileRange.maxY))) { + return true; + } + if (tileRange.getWidth() > width && + testTileRange.intersects(extentTileRange)) { + return true; + } + } + } + } + return false; +}; + +/** + * An implementation of Google Maps' MVCArray. + * @see https://developers.google.com/maps/documentation/javascript/reference + */ + +goog.provide('ol.Collection'); +goog.provide('ol.CollectionEvent'); +goog.provide('ol.CollectionEventType'); + +goog.require('ol.events.Event'); +goog.require('ol.Object'); + + +/** + * @enum {string} + */ +ol.CollectionEventType = { + /** + * Triggered when an item is added to the collection. + * @event ol.CollectionEvent#add + * @api stable + */ + ADD: 'add', + /** + * Triggered when an item is removed from the collection. + * @event ol.CollectionEvent#remove + * @api stable + */ + REMOVE: 'remove' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.Collection} instances are instances of this + * type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.CollectionEvent} + * @param {ol.CollectionEventType} type Type. + * @param {*=} opt_element Element. + * @param {Object=} opt_target Target. + */ +ol.CollectionEvent = function(type, opt_element, opt_target) { + + ol.events.Event.call(this, type, opt_target); + + /** + * The element that is added to or removed from the collection. + * @type {*} + * @api stable + */ + this.element = opt_element; + +}; +ol.inherits(ol.CollectionEvent, ol.events.Event); + + +/** + * @enum {string} + */ +ol.CollectionProperty = { + LENGTH: 'length' +}; + + +/** + * @classdesc + * An expanded version of standard JS Array, adding convenience methods for + * manipulation. Add and remove changes to the Collection trigger a Collection + * event. Note that this does not cover changes to the objects _within_ the + * Collection; they trigger events on the appropriate object, not on the + * Collection as a whole. + * + * @constructor + * @extends {ol.Object} + * @fires ol.CollectionEvent + * @param {!Array.<T>=} opt_array Array. + * @template T + * @api stable + */ +ol.Collection = function(opt_array) { + + ol.Object.call(this); + + /** + * @private + * @type {!Array.<T>} + */ + this.array_ = opt_array ? opt_array : []; + + this.updateLength_(); + +}; +ol.inherits(ol.Collection, ol.Object); + + +/** + * Remove all elements from the collection. + * @api stable + */ +ol.Collection.prototype.clear = function() { + while (this.getLength() > 0) { + this.pop(); + } +}; + + +/** + * Add elements to the collection. This pushes each item in the provided array + * to the end of the collection. + * @param {!Array.<T>} arr Array. + * @return {ol.Collection.<T>} This collection. + * @api stable + */ +ol.Collection.prototype.extend = function(arr) { + var i, ii; + for (i = 0, ii = arr.length; i < ii; ++i) { + this.push(arr[i]); + } + return this; +}; + + +/** + * Iterate over each element, calling the provided callback. + * @param {function(this: S, T, number, Array.<T>): *} f The function to call + * for every element. This function takes 3 arguments (the element, the + * index and the array). The return value is ignored. + * @param {S=} opt_this The object to use as `this` in `f`. + * @template S + * @api stable + */ +ol.Collection.prototype.forEach = function(f, opt_this) { + this.array_.forEach(f, opt_this); +}; + + +/** + * Get a reference to the underlying Array object. Warning: if the array + * is mutated, no events will be dispatched by the collection, and the + * collection's "length" property won't be in sync with the actual length + * of the array. + * @return {!Array.<T>} Array. + * @api stable + */ +ol.Collection.prototype.getArray = function() { + return this.array_; +}; + + +/** + * Get the element at the provided index. + * @param {number} index Index. + * @return {T} Element. + * @api stable + */ +ol.Collection.prototype.item = function(index) { + return this.array_[index]; +}; + + +/** + * Get the length of this collection. + * @return {number} The length of the array. + * @observable + * @api stable + */ +ol.Collection.prototype.getLength = function() { + return /** @type {number} */ (this.get(ol.CollectionProperty.LENGTH)); +}; + + +/** + * Insert an element at the provided index. + * @param {number} index Index. + * @param {T} elem Element. + * @api stable + */ +ol.Collection.prototype.insertAt = function(index, elem) { + this.array_.splice(index, 0, elem); + this.updateLength_(); + this.dispatchEvent( + new ol.CollectionEvent(ol.CollectionEventType.ADD, elem, this)); +}; + + +/** + * Remove the last element of the collection and return it. + * Return `undefined` if the collection is empty. + * @return {T|undefined} Element. + * @api stable + */ +ol.Collection.prototype.pop = function() { + return this.removeAt(this.getLength() - 1); +}; + + +/** + * Insert the provided element at the end of the collection. + * @param {T} elem Element. + * @return {number} Length. + * @api stable + */ +ol.Collection.prototype.push = function(elem) { + var n = this.array_.length; + this.insertAt(n, elem); + return n; +}; + + +/** + * Remove the first occurrence of an element from the collection. + * @param {T} elem Element. + * @return {T|undefined} The removed element or undefined if none found. + * @api stable + */ +ol.Collection.prototype.remove = function(elem) { + var arr = this.array_; + var i, ii; + for (i = 0, ii = arr.length; i < ii; ++i) { + if (arr[i] === elem) { + return this.removeAt(i); + } + } + return undefined; +}; + + +/** + * Remove the element at the provided index and return it. + * Return `undefined` if the collection does not contain this index. + * @param {number} index Index. + * @return {T|undefined} Value. + * @api stable + */ +ol.Collection.prototype.removeAt = function(index) { + var prev = this.array_[index]; + this.array_.splice(index, 1); + this.updateLength_(); + this.dispatchEvent( + new ol.CollectionEvent(ol.CollectionEventType.REMOVE, prev, this)); + return prev; +}; + + +/** + * Set the element at the provided index. + * @param {number} index Index. + * @param {T} elem Element. + * @api stable + */ +ol.Collection.prototype.setAt = function(index, elem) { + var n = this.getLength(); + if (index < n) { + var prev = this.array_[index]; + this.array_[index] = elem; + this.dispatchEvent( + new ol.CollectionEvent(ol.CollectionEventType.REMOVE, prev, this)); + this.dispatchEvent( + new ol.CollectionEvent(ol.CollectionEventType.ADD, elem, this)); + } else { + var j; + for (j = n; j < index; ++j) { + this.insertAt(j, undefined); + } + this.insertAt(index, elem); + } +}; + + +/** + * @private + */ +ol.Collection.prototype.updateLength_ = function() { + this.set(ol.CollectionProperty.LENGTH, this.array_.length); +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Names of standard colors with their associated hex values. + */ + +goog.provide('goog.color.names'); + + +/** + * A map that contains a lot of colors that are recognised by various browsers. + * This list is way larger than the minimal one dictated by W3C. + * The keys of this map are the lowercase "readable" names of the colors, while + * the values are the "hex" values. + * + * @type {!Object<string, string>} + */ +goog.color.names = { + 'aliceblue': '#f0f8ff', + 'antiquewhite': '#faebd7', + 'aqua': '#00ffff', + 'aquamarine': '#7fffd4', + 'azure': '#f0ffff', + 'beige': '#f5f5dc', + 'bisque': '#ffe4c4', + 'black': '#000000', + 'blanchedalmond': '#ffebcd', + 'blue': '#0000ff', + 'blueviolet': '#8a2be2', + 'brown': '#a52a2a', + 'burlywood': '#deb887', + 'cadetblue': '#5f9ea0', + 'chartreuse': '#7fff00', + 'chocolate': '#d2691e', + 'coral': '#ff7f50', + 'cornflowerblue': '#6495ed', + 'cornsilk': '#fff8dc', + 'crimson': '#dc143c', + 'cyan': '#00ffff', + 'darkblue': '#00008b', + 'darkcyan': '#008b8b', + 'darkgoldenrod': '#b8860b', + 'darkgray': '#a9a9a9', + 'darkgreen': '#006400', + 'darkgrey': '#a9a9a9', + 'darkkhaki': '#bdb76b', + 'darkmagenta': '#8b008b', + 'darkolivegreen': '#556b2f', + 'darkorange': '#ff8c00', + 'darkorchid': '#9932cc', + 'darkred': '#8b0000', + 'darksalmon': '#e9967a', + 'darkseagreen': '#8fbc8f', + 'darkslateblue': '#483d8b', + 'darkslategray': '#2f4f4f', + 'darkslategrey': '#2f4f4f', + 'darkturquoise': '#00ced1', + 'darkviolet': '#9400d3', + 'deeppink': '#ff1493', + 'deepskyblue': '#00bfff', + 'dimgray': '#696969', + 'dimgrey': '#696969', + 'dodgerblue': '#1e90ff', + 'firebrick': '#b22222', + 'floralwhite': '#fffaf0', + 'forestgreen': '#228b22', + 'fuchsia': '#ff00ff', + 'gainsboro': '#dcdcdc', + 'ghostwhite': '#f8f8ff', + 'gold': '#ffd700', + 'goldenrod': '#daa520', + 'gray': '#808080', + 'green': '#008000', + 'greenyellow': '#adff2f', + 'grey': '#808080', + 'honeydew': '#f0fff0', + 'hotpink': '#ff69b4', + 'indianred': '#cd5c5c', + 'indigo': '#4b0082', + 'ivory': '#fffff0', + 'khaki': '#f0e68c', + 'lavender': '#e6e6fa', + 'lavenderblush': '#fff0f5', + 'lawngreen': '#7cfc00', + 'lemonchiffon': '#fffacd', + 'lightblue': '#add8e6', + 'lightcoral': '#f08080', + 'lightcyan': '#e0ffff', + 'lightgoldenrodyellow': '#fafad2', + 'lightgray': '#d3d3d3', + 'lightgreen': '#90ee90', + 'lightgrey': '#d3d3d3', + 'lightpink': '#ffb6c1', + 'lightsalmon': '#ffa07a', + 'lightseagreen': '#20b2aa', + 'lightskyblue': '#87cefa', + 'lightslategray': '#778899', + 'lightslategrey': '#778899', + 'lightsteelblue': '#b0c4de', + 'lightyellow': '#ffffe0', + 'lime': '#00ff00', + 'limegreen': '#32cd32', + 'linen': '#faf0e6', + 'magenta': '#ff00ff', + 'maroon': '#800000', + 'mediumaquamarine': '#66cdaa', + 'mediumblue': '#0000cd', + 'mediumorchid': '#ba55d3', + 'mediumpurple': '#9370db', + 'mediumseagreen': '#3cb371', + 'mediumslateblue': '#7b68ee', + 'mediumspringgreen': '#00fa9a', + 'mediumturquoise': '#48d1cc', + 'mediumvioletred': '#c71585', + 'midnightblue': '#191970', + 'mintcream': '#f5fffa', + 'mistyrose': '#ffe4e1', + 'moccasin': '#ffe4b5', + 'navajowhite': '#ffdead', + 'navy': '#000080', + 'oldlace': '#fdf5e6', + 'olive': '#808000', + 'olivedrab': '#6b8e23', + 'orange': '#ffa500', + 'orangered': '#ff4500', + 'orchid': '#da70d6', + 'palegoldenrod': '#eee8aa', + 'palegreen': '#98fb98', + 'paleturquoise': '#afeeee', + 'palevioletred': '#db7093', + 'papayawhip': '#ffefd5', + 'peachpuff': '#ffdab9', + 'peru': '#cd853f', + 'pink': '#ffc0cb', + 'plum': '#dda0dd', + 'powderblue': '#b0e0e6', + 'purple': '#800080', + 'red': '#ff0000', + 'rosybrown': '#bc8f8f', + 'royalblue': '#4169e1', + 'saddlebrown': '#8b4513', + 'salmon': '#fa8072', + 'sandybrown': '#f4a460', + 'seagreen': '#2e8b57', + 'seashell': '#fff5ee', + 'sienna': '#a0522d', + 'silver': '#c0c0c0', + 'skyblue': '#87ceeb', + 'slateblue': '#6a5acd', + 'slategray': '#708090', + 'slategrey': '#708090', + 'snow': '#fffafa', + 'springgreen': '#00ff7f', + 'steelblue': '#4682b4', + 'tan': '#d2b48c', + 'teal': '#008080', + 'thistle': '#d8bfd8', + 'tomato': '#ff6347', + 'turquoise': '#40e0d0', + 'violet': '#ee82ee', + 'wheat': '#f5deb3', + 'white': '#ffffff', + 'whitesmoke': '#f5f5f5', + 'yellow': '#ffff00', + 'yellowgreen': '#9acd32' +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for manipulating arrays. + * + * @author arv@google.com (Erik Arvidsson) + */ + + +goog.provide('goog.array'); + +goog.require('goog.asserts'); + + +/** + * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should + * rely on Array.prototype functions, if available. + * + * The Array.prototype functions can be defined by external libraries like + * Prototype and setting this flag to false forces closure to use its own + * goog.array implementation. + * + * If your javascript can be loaded by a third party site and you are wary about + * relying on the prototype functions, specify + * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler. + * + * Setting goog.TRUSTED_SITE to false will automatically set + * NATIVE_ARRAY_PROTOTYPES to false. + */ +goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE); + + +/** + * @define {boolean} If true, JSCompiler will use the native implementation of + * array functions where appropriate (e.g., {@code Array#filter}) and remove the + * unused pure JS implementation. + */ +goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false); + + +/** + * Returns the last element in an array without removing it. + * Same as goog.array.last. + * @param {IArrayLike<T>|string} array The array. + * @return {T} Last item in array. + * @template T + */ +goog.array.peek = function(array) { + return array[array.length - 1]; +}; + + +/** + * Returns the last element in an array without removing it. + * Same as goog.array.peek. + * @param {IArrayLike<T>|string} array The array. + * @return {T} Last item in array. + * @template T + */ +goog.array.last = goog.array.peek; + +// NOTE(arv): Since most of the array functions are generic it allows you to +// pass an array-like object. Strings have a length and are considered array- +// like. However, the 'in' operator does not work on strings so we cannot just +// use the array path even if the browser supports indexing into strings. We +// therefore end up splitting the string. + + +/** + * Returns the index of the first element of an array with a specified value, or + * -1 if the element is not present in the array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof} + * + * @param {IArrayLike<T>|string} arr The array to be searched. + * @param {T} obj The object for which we are searching. + * @param {number=} opt_fromIndex The index at which to start the search. If + * omitted the search starts at index 0. + * @return {number} The index of the first matching array element. + * @template T + */ +goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.indexOf) ? + function(arr, obj, opt_fromIndex) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.indexOf.call(arr, obj, opt_fromIndex); + } : + function(arr, obj, opt_fromIndex) { + var fromIndex = opt_fromIndex == null ? + 0 : + (opt_fromIndex < 0 ? Math.max(0, arr.length + opt_fromIndex) : + opt_fromIndex); + + if (goog.isString(arr)) { + // Array.prototype.indexOf uses === so only strings should be found. + if (!goog.isString(obj) || obj.length != 1) { + return -1; + } + return arr.indexOf(obj, fromIndex); + } + + for (var i = fromIndex; i < arr.length; i++) { + if (i in arr && arr[i] === obj) return i; + } + return -1; + }; + + +/** + * Returns the index of the last element of an array with a specified value, or + * -1 if the element is not present in the array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof} + * + * @param {!IArrayLike<T>|string} arr The array to be searched. + * @param {T} obj The object for which we are searching. + * @param {?number=} opt_fromIndex The index at which to start the search. If + * omitted the search starts at the end of the array. + * @return {number} The index of the last matching array element. + * @template T + */ +goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.lastIndexOf) ? + function(arr, obj, opt_fromIndex) { + goog.asserts.assert(arr.length != null); + + // Firefox treats undefined and null as 0 in the fromIndex argument which + // leads it to always return -1 + var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; + return Array.prototype.lastIndexOf.call(arr, obj, fromIndex); + } : + function(arr, obj, opt_fromIndex) { + var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; + + if (fromIndex < 0) { + fromIndex = Math.max(0, arr.length + fromIndex); + } + + if (goog.isString(arr)) { + // Array.prototype.lastIndexOf uses === so only strings should be found. + if (!goog.isString(obj) || obj.length != 1) { + return -1; + } + return arr.lastIndexOf(obj, fromIndex); + } + + for (var i = fromIndex; i >= 0; i--) { + if (i in arr && arr[i] === obj) return i; + } + return -1; + }; + + +/** + * Calls a function for each element in an array. Skips holes in the array. + * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach} + * + * @param {IArrayLike<T>|string} arr Array or array like object over + * which to iterate. + * @param {?function(this: S, T, number, ?): ?} f The function to call for every + * element. This function takes 3 arguments (the element, the index and the + * array). The return value is ignored. + * @param {S=} opt_obj The object to be used as the value of 'this' within f. + * @template T,S + */ +goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.forEach) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + Array.prototype.forEach.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr); + } + } + }; + + +/** + * Calls a function for each element in an array, starting from the last + * element rather than the first. + * + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this: S, T, number, ?): ?} f The function to call for every + * element. This function + * takes 3 arguments (the element, the index and the array). The return + * value is ignored. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @template T,S + */ +goog.array.forEachRight = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = l - 1; i >= 0; --i) { + if (i in arr2) { + f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr); + } + } +}; + + +/** + * Calls a function for each element in an array, and if the function returns + * true adds the element to a new array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-filter} + * + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?):boolean} f The function to call for + * every element. This function + * takes 3 arguments (the element, the index and the array) and must + * return a Boolean. If the return value is true the element is added to the + * result array. If it is false the element is not included. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {!Array<T>} a new array in which only elements that passed the test + * are present. + * @template T,S + */ +goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.filter) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.filter.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var res = []; + var resLength = 0; + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + var val = arr2[i]; // in case f mutates arr2 + if (f.call(/** @type {?} */ (opt_obj), val, i, arr)) { + res[resLength++] = val; + } + } + } + return res; + }; + + +/** + * Calls a function for each element in an array and inserts the result into a + * new array. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-map} + * + * @param {IArrayLike<VALUE>|string} arr Array or array like object + * over which to iterate. + * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call + * for every element. This function takes 3 arguments (the element, + * the index and the array) and should return something. The result will be + * inserted into a new array. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within f. + * @return {!Array<RESULT>} a new array with the results from f. + * @template THIS, VALUE, RESULT + */ +goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.map) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.map.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var res = new Array(l); + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2) { + res[i] = f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr); + } + } + return res; + }; + + +/** + * Passes every element of an array into a function and accumulates the result. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce} + * + * For example: + * var a = [1, 2, 3, 4]; + * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0); + * returns 10 + * + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {function(this:S, R, T, number, ?) : R} f The function to call for + * every element. This function + * takes 4 arguments (the function's previous result or the initial value, + * the value of the current array element, the current array index, and the + * array itself) + * function(previousValue, currentValue, index, array). + * @param {?} val The initial value to pass into the function on the first call. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {R} Result of evaluating f repeatedly across the values of the array. + * @template T,S,R + */ +goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduce) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return Array.prototype.reduce.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEach(arr, function(val, index) { + rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr); + }); + return rval; + }; + + +/** + * Passes every element of an array into a function and accumulates the result, + * starting from the last element and working towards the first. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright} + * + * For example: + * var a = ['a', 'b', 'c']; + * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, ''); + * returns 'cba' + * + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, R, T, number, ?) : R} f The function to call for + * every element. This function + * takes 4 arguments (the function's previous result or the initial value, + * the value of the current array element, the current array index, and the + * array itself) + * function(previousValue, currentValue, index, array). + * @param {?} val The initial value to pass into the function on the first call. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {R} Object returned as a result of evaluating f repeatedly across the + * values of the array. + * @template T,S,R + */ +goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.reduceRight) ? + function(arr, f, val, opt_obj) { + goog.asserts.assert(arr.length != null); + goog.asserts.assert(f != null); + if (opt_obj) { + f = goog.bind(f, opt_obj); + } + return Array.prototype.reduceRight.call(arr, f, val); + } : + function(arr, f, val, opt_obj) { + var rval = val; + goog.array.forEachRight(arr, function(val, index) { + rval = f.call(/** @type {?} */ (opt_obj), rval, val, index, arr); + }); + return rval; + }; + + +/** + * Calls f for each element of an array. If any call returns true, some() + * returns true (without checking the remaining elements). If all calls + * return false, some() returns false. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-some} + * + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call for + * for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a boolean. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {boolean} true if any element passes the test. + * @template T,S + */ +goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.some) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.some.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return true; + } + } + return false; + }; + + +/** + * Call f for each element of an array. If all calls return true, every() + * returns true. If any call returns false, every() returns false and + * does not continue to check the remaining elements. + * + * See {@link http://tinyurl.com/developer-mozilla-org-array-every} + * + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call for + * for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a boolean. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within f. + * @return {boolean} false if any element fails the test. + * @template T,S + */ +goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES && + (goog.array.ASSUME_NATIVE_FUNCTIONS || Array.prototype.every) ? + function(arr, f, opt_obj) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.every.call(arr, f, opt_obj); + } : + function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && !f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return false; + } + } + return true; + }; + + +/** + * Counts the array elements that fulfill the predicate, i.e. for which the + * callback function returns true. Skips holes in the array. + * + * @param {!IArrayLike<T>|string} arr Array or array like object + * over which to iterate. + * @param {function(this: S, T, number, ?): boolean} f The function to call for + * every element. Takes 3 arguments (the element, the index and the array). + * @param {S=} opt_obj The object to be used as the value of 'this' within f. + * @return {number} The number of the matching elements. + * @template T,S + */ +goog.array.count = function(arr, f, opt_obj) { + var count = 0; + goog.array.forEach(arr, function(element, index, arr) { + if (f.call(/** @type {?} */ (opt_obj), element, index, arr)) { + ++count; + } + }, opt_obj); + return count; +}; + + +/** + * Search an array for the first element that satisfies a given condition and + * return that element. + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {T|null} The first array element that passes the test, or null if no + * element is found. + * @template T,S + */ +goog.array.find = function(arr, f, opt_obj) { + var i = goog.array.findIndex(arr, f, opt_obj); + return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; +}; + + +/** + * Search an array for the first element that satisfies a given condition and + * return its index. + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call for + * every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The index of the first array element that passes the test, + * or -1 if no element is found. + * @template T,S + */ +goog.array.findIndex = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = 0; i < l; i++) { + if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return i; + } + } + return -1; +}; + + +/** + * Search an array (in reverse order) for the last element that satisfies a + * given condition and return that element. + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {T|null} The last array element that passes the test, or null if no + * element is found. + * @template T,S + */ +goog.array.findRight = function(arr, f, opt_obj) { + var i = goog.array.findIndexRight(arr, f, opt_obj); + return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; +}; + + +/** + * Search an array (in reverse order) for the last element that satisfies a + * given condition and return its index. + * @param {IArrayLike<T>|string} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The index of the last array element that passes the test, + * or -1 if no element is found. + * @template T,S + */ +goog.array.findIndexRight = function(arr, f, opt_obj) { + var l = arr.length; // must be fixed during loop... see docs + var arr2 = goog.isString(arr) ? arr.split('') : arr; + for (var i = l - 1; i >= 0; i--) { + if (i in arr2 && f.call(/** @type {?} */ (opt_obj), arr2[i], i, arr)) { + return i; + } + } + return -1; +}; + + +/** + * Whether the array contains the given object. + * @param {IArrayLike<?>|string} arr The array to test for the presence of the + * element. + * @param {*} obj The object for which to test. + * @return {boolean} true if obj is present. + */ +goog.array.contains = function(arr, obj) { + return goog.array.indexOf(arr, obj) >= 0; +}; + + +/** + * Whether the array is empty. + * @param {IArrayLike<?>|string} arr The array to test. + * @return {boolean} true if empty. + */ +goog.array.isEmpty = function(arr) { + return arr.length == 0; +}; + + +/** + * Clears the array. + * @param {IArrayLike<?>} arr Array or array like object to clear. + */ +goog.array.clear = function(arr) { + // For non real arrays we don't have the magic length so we delete the + // indices. + if (!goog.isArray(arr)) { + for (var i = arr.length - 1; i >= 0; i--) { + delete arr[i]; + } + } + arr.length = 0; +}; + + +/** + * Pushes an item into an array, if it's not already in the array. + * @param {Array<T>} arr Array into which to insert the item. + * @param {T} obj Value to add. + * @template T + */ +goog.array.insert = function(arr, obj) { + if (!goog.array.contains(arr, obj)) { + arr.push(obj); + } +}; + + +/** + * Inserts an object at the given index of the array. + * @param {IArrayLike<?>} arr The array to modify. + * @param {*} obj The object to insert. + * @param {number=} opt_i The index at which to insert the object. If omitted, + * treated as 0. A negative index is counted from the end of the array. + */ +goog.array.insertAt = function(arr, obj, opt_i) { + goog.array.splice(arr, opt_i, 0, obj); +}; + + +/** + * Inserts at the given index of the array, all elements of another array. + * @param {IArrayLike<?>} arr The array to modify. + * @param {IArrayLike<?>} elementsToAdd The array of elements to add. + * @param {number=} opt_i The index at which to insert the object. If omitted, + * treated as 0. A negative index is counted from the end of the array. + */ +goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) { + goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd); +}; + + +/** + * Inserts an object into an array before a specified object. + * @param {Array<T>} arr The array to modify. + * @param {T} obj The object to insert. + * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2 + * is omitted or not found, obj is inserted at the end of the array. + * @template T + */ +goog.array.insertBefore = function(arr, obj, opt_obj2) { + var i; + if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) { + arr.push(obj); + } else { + goog.array.insertAt(arr, obj, i); + } +}; + + +/** + * Removes the first occurrence of a particular value from an array. + * @param {IArrayLike<T>} arr Array from which to remove + * value. + * @param {T} obj Object to remove. + * @return {boolean} True if an element was removed. + * @template T + */ +goog.array.remove = function(arr, obj) { + var i = goog.array.indexOf(arr, obj); + var rv; + if ((rv = i >= 0)) { + goog.array.removeAt(arr, i); + } + return rv; +}; + + +/** + * Removes the last occurrence of a particular value from an array. + * @param {!IArrayLike<T>} arr Array from which to remove value. + * @param {T} obj Object to remove. + * @return {boolean} True if an element was removed. + * @template T + */ +goog.array.removeLast = function(arr, obj) { + var i = goog.array.lastIndexOf(arr, obj); + if (i >= 0) { + goog.array.removeAt(arr, i); + return true; + } + return false; +}; + + +/** + * Removes from an array the element at index i + * @param {IArrayLike<?>} arr Array or array like object from which to + * remove value. + * @param {number} i The index to remove. + * @return {boolean} True if an element was removed. + */ +goog.array.removeAt = function(arr, i) { + goog.asserts.assert(arr.length != null); + + // use generic form of splice + // splice returns the removed items and if successful the length of that + // will be 1 + return Array.prototype.splice.call(arr, i, 1).length == 1; +}; + + +/** + * Removes the first value that satisfies the given condition. + * @param {IArrayLike<T>} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {boolean} True if an element was removed. + * @template T,S + */ +goog.array.removeIf = function(arr, f, opt_obj) { + var i = goog.array.findIndex(arr, f, opt_obj); + if (i >= 0) { + goog.array.removeAt(arr, i); + return true; + } + return false; +}; + + +/** + * Removes all values that satisfy the given condition. + * @param {IArrayLike<T>} arr Array or array + * like object over which to iterate. + * @param {?function(this:S, T, number, ?) : boolean} f The function to call + * for every element. This function + * takes 3 arguments (the element, the index and the array) and should + * return a boolean. + * @param {S=} opt_obj An optional "this" context for the function. + * @return {number} The number of items removed + * @template T,S + */ +goog.array.removeAllIf = function(arr, f, opt_obj) { + var removedCount = 0; + goog.array.forEachRight(arr, function(val, index) { + if (f.call(/** @type {?} */ (opt_obj), val, index, arr)) { + if (goog.array.removeAt(arr, index)) { + removedCount++; + } + } + }); + return removedCount; +}; + + +/** + * Returns a new array that is the result of joining the arguments. If arrays + * are passed then their items are added, however, if non-arrays are passed they + * will be added to the return array as is. + * + * Note that ArrayLike objects will be added as is, rather than having their + * items added. + * + * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4] + * goog.array.concat(0, [1, 2]) -> [0, 1, 2] + * goog.array.concat([1, 2], null) -> [1, 2, null] + * + * There is bug in all current versions of IE (6, 7 and 8) where arrays created + * in an iframe become corrupted soon (not immediately) after the iframe is + * destroyed. This is common if loading data via goog.net.IframeIo, for example. + * This corruption only affects the concat method which will start throwing + * Catastrophic Errors (#-2147418113). + * + * See http://endoflow.com/scratch/corrupted-arrays.html for a test case. + * + * Internally goog.array should use this, so that all methods will continue to + * work on these broken array objects. + * + * @param {...*} var_args Items to concatenate. Arrays will have each item + * added, while primitives and objects will be added as is. + * @return {!Array<?>} The new resultant array. + */ +goog.array.concat = function(var_args) { + return Array.prototype.concat.apply(Array.prototype, arguments); +}; + + +/** + * Returns a new array that contains the contents of all the arrays passed. + * @param {...!Array<T>} var_args + * @return {!Array<T>} + * @template T + */ +goog.array.join = function(var_args) { + return Array.prototype.concat.apply(Array.prototype, arguments); +}; + + +/** + * Converts an object to an array. + * @param {IArrayLike<T>|string} object The object to convert to an + * array. + * @return {!Array<T>} The object converted into an array. If object has a + * length property, every property indexed with a non-negative number + * less than length will be included in the result. If object does not + * have a length property, an empty array will be returned. + * @template T + */ +goog.array.toArray = function(object) { + var length = object.length; + + // If length is not a number the following it false. This case is kept for + // backwards compatibility since there are callers that pass objects that are + // not array like. + if (length > 0) { + var rv = new Array(length); + for (var i = 0; i < length; i++) { + rv[i] = object[i]; + } + return rv; + } + return []; +}; + + +/** + * Does a shallow copy of an array. + * @param {IArrayLike<T>|string} arr Array or array-like object to + * clone. + * @return {!Array<T>} Clone of the input array. + * @template T + */ +goog.array.clone = goog.array.toArray; + + +/** + * Extends an array with another array, element, or "array like" object. + * This function operates 'in-place', it does not create a new Array. + * + * Example: + * var a = []; + * goog.array.extend(a, [0, 1]); + * a; // [0, 1] + * goog.array.extend(a, 2); + * a; // [0, 1, 2] + * + * @param {Array<VALUE>} arr1 The array to modify. + * @param {...(Array<VALUE>|VALUE)} var_args The elements or arrays of elements + * to add to arr1. + * @template VALUE + */ +goog.array.extend = function(arr1, var_args) { + for (var i = 1; i < arguments.length; i++) { + var arr2 = arguments[i]; + if (goog.isArrayLike(arr2)) { + var len1 = arr1.length || 0; + var len2 = arr2.length || 0; + arr1.length = len1 + len2; + for (var j = 0; j < len2; j++) { + arr1[len1 + j] = arr2[j]; + } + } else { + arr1.push(arr2); + } + } +}; + + +/** + * Adds or removes elements from an array. This is a generic version of Array + * splice. This means that it might work on other objects similar to arrays, + * such as the arguments object. + * + * @param {IArrayLike<T>} arr The array to modify. + * @param {number|undefined} index The index at which to start changing the + * array. If not defined, treated as 0. + * @param {number} howMany How many elements to remove (0 means no removal. A + * value below 0 is treated as zero and so is any other non number. Numbers + * are floored). + * @param {...T} var_args Optional, additional elements to insert into the + * array. + * @return {!Array<T>} the removed elements. + * @template T + */ +goog.array.splice = function(arr, index, howMany, var_args) { + goog.asserts.assert(arr.length != null); + + return Array.prototype.splice.apply(arr, goog.array.slice(arguments, 1)); +}; + + +/** + * Returns a new array from a segment of an array. This is a generic version of + * Array slice. This means that it might work on other objects similar to + * arrays, such as the arguments object. + * + * @param {IArrayLike<T>|string} arr The array from + * which to copy a segment. + * @param {number} start The index of the first element to copy. + * @param {number=} opt_end The index after the last element to copy. + * @return {!Array<T>} A new array containing the specified segment of the + * original array. + * @template T + */ +goog.array.slice = function(arr, start, opt_end) { + goog.asserts.assert(arr.length != null); + + // passing 1 arg to slice is not the same as passing 2 where the second is + // null or undefined (in that case the second argument is treated as 0). + // we could use slice on the arguments object and then use apply instead of + // testing the length + if (arguments.length <= 2) { + return Array.prototype.slice.call(arr, start); + } else { + return Array.prototype.slice.call(arr, start, opt_end); + } +}; + + +/** + * Removes all duplicates from an array (retaining only the first + * occurrence of each array element). This function modifies the + * array in place and doesn't change the order of the non-duplicate items. + * + * For objects, duplicates are identified as having the same unique ID as + * defined by {@link goog.getUid}. + * + * Alternatively you can specify a custom hash function that returns a unique + * value for each item in the array it should consider unique. + * + * Runtime: N, + * Worstcase space: 2N (no dupes) + * + * @param {IArrayLike<T>} arr The array from which to remove + * duplicates. + * @param {Array=} opt_rv An optional array in which to return the results, + * instead of performing the removal inplace. If specified, the original + * array will remain unchanged. + * @param {function(T):string=} opt_hashFn An optional function to use to + * apply to every item in the array. This function should return a unique + * value for each item in the array it should consider unique. + * @template T + */ +goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) { + var returnArray = opt_rv || arr; + var defaultHashFn = function(item) { + // Prefix each type with a single character representing the type to + // prevent conflicting keys (e.g. true and 'true'). + return goog.isObject(item) ? 'o' + goog.getUid(item) : + (typeof item).charAt(0) + item; + }; + var hashFn = opt_hashFn || defaultHashFn; + + var seen = {}, cursorInsert = 0, cursorRead = 0; + while (cursorRead < arr.length) { + var current = arr[cursorRead++]; + var key = hashFn(current); + if (!Object.prototype.hasOwnProperty.call(seen, key)) { + seen[key] = true; + returnArray[cursorInsert++] = current; + } + } + returnArray.length = cursorInsert; +}; + + +/** + * Searches the specified array for the specified target using the binary + * search algorithm. If no opt_compareFn is specified, elements are compared + * using <code>goog.array.defaultCompare</code>, which compares the elements + * using the built in < and > operators. This will produce the expected + * behavior for homogeneous arrays of String(s) and Number(s). The array + * specified <b>must</b> be sorted in ascending order (as defined by the + * comparison function). If the array is not sorted, results are undefined. + * If the array contains multiple instances of the specified target value, any + * of these instances may be found. + * + * Runtime: O(log n) + * + * @param {IArrayLike<VALUE>} arr The array to be searched. + * @param {TARGET} target The sought value. + * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {number} Lowest index of the target value if found, otherwise + * (-(insertion point) - 1). The insertion point is where the value should + * be inserted into arr to preserve the sorted property. Return value >= 0 + * iff target is found. + * @template TARGET, VALUE + */ +goog.array.binarySearch = function(arr, target, opt_compareFn) { + return goog.array.binarySearch_( + arr, opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */, + target); +}; + + +/** + * Selects an index in the specified array using the binary search algorithm. + * The evaluator receives an element and determines whether the desired index + * is before, at, or after it. The evaluator must be consistent (formally, + * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign) + * must be monotonically non-increasing). + * + * Runtime: O(log n) + * + * @param {IArrayLike<VALUE>} arr The array to be searched. + * @param {function(this:THIS, VALUE, number, ?): number} evaluator + * Evaluator function that receives 3 arguments (the element, the index and + * the array). Should return a negative number, zero, or a positive number + * depending on whether the desired index is before, at, or after the + * element passed to it. + * @param {THIS=} opt_obj The object to be used as the value of 'this' + * within evaluator. + * @return {number} Index of the leftmost element matched by the evaluator, if + * such exists; otherwise (-(insertion point) - 1). The insertion point is + * the index of the first element for which the evaluator returns negative, + * or arr.length if no such element exists. The return value is non-negative + * iff a match is found. + * @template THIS, VALUE + */ +goog.array.binarySelect = function(arr, evaluator, opt_obj) { + return goog.array.binarySearch_( + arr, evaluator, true /* isEvaluator */, undefined /* opt_target */, + opt_obj); +}; + + +/** + * Implementation of a binary search algorithm which knows how to use both + * comparison functions and evaluators. If an evaluator is provided, will call + * the evaluator with the given optional data object, conforming to the + * interface defined in binarySelect. Otherwise, if a comparison function is + * provided, will call the comparison function against the given data object. + * + * This implementation purposefully does not use goog.bind or goog.partial for + * performance reasons. + * + * Runtime: O(log n) + * + * @param {IArrayLike<?>} arr The array to be searched. + * @param {function(?, ?, ?): number | function(?, ?): number} compareFn + * Either an evaluator or a comparison function, as defined by binarySearch + * and binarySelect above. + * @param {boolean} isEvaluator Whether the function is an evaluator or a + * comparison function. + * @param {?=} opt_target If the function is a comparison function, then + * this is the target to binary search for. + * @param {Object=} opt_selfObj If the function is an evaluator, this is an + * optional this object for the evaluator. + * @return {number} Lowest index of the target value if found, otherwise + * (-(insertion point) - 1). The insertion point is where the value should + * be inserted into arr to preserve the sorted property. Return value >= 0 + * iff target is found. + * @private + */ +goog.array.binarySearch_ = function( + arr, compareFn, isEvaluator, opt_target, opt_selfObj) { + var left = 0; // inclusive + var right = arr.length; // exclusive + var found; + while (left < right) { + var middle = (left + right) >> 1; + var compareResult; + if (isEvaluator) { + compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr); + } else { + // NOTE(dimvar): To avoid this cast, we'd have to use function overloading + // for the type of binarySearch_, which the type system can't express yet. + compareResult = /** @type {function(?, ?): number} */ (compareFn)( + opt_target, arr[middle]); + } + if (compareResult > 0) { + left = middle + 1; + } else { + right = middle; + // We are looking for the lowest index so we can't return immediately. + found = !compareResult; + } + } + // left is the index if found, or the insertion point otherwise. + // ~left is a shorthand for -left - 1. + return found ? left : ~left; +}; + + +/** + * Sorts the specified array into ascending order. If no opt_compareFn is + * specified, elements are compared using + * <code>goog.array.defaultCompare</code>, which compares the elements using + * the built in < and > operators. This will produce the expected behavior + * for homogeneous arrays of String(s) and Number(s), unlike the native sort, + * but will give unpredictable results for heterogeneous lists of strings and + * numbers with different numbers of digits. + * + * This sort is not guaranteed to be stable. + * + * Runtime: Same as <code>Array.prototype.sort</code> + * + * @param {Array<T>} arr The array to be sorted. + * @param {?function(T,T):number=} opt_compareFn Optional comparison + * function by which the + * array is to be ordered. Should take 2 arguments to compare, and return a + * negative number, zero, or a positive number depending on whether the + * first argument is less than, equal to, or greater than the second. + * @template T + */ +goog.array.sort = function(arr, opt_compareFn) { + // TODO(arv): Update type annotation since null is not accepted. + arr.sort(opt_compareFn || goog.array.defaultCompare); +}; + + +/** + * Sorts the specified array into ascending order in a stable way. If no + * opt_compareFn is specified, elements are compared using + * <code>goog.array.defaultCompare</code>, which compares the elements using + * the built in < and > operators. This will produce the expected behavior + * for homogeneous arrays of String(s) and Number(s). + * + * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional + * O(n) overhead of copying the array twice. + * + * @param {Array<T>} arr The array to be sorted. + * @param {?function(T, T): number=} opt_compareFn Optional comparison function + * by which the array is to be ordered. Should take 2 arguments to compare, + * and return a negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template T + */ +goog.array.stableSort = function(arr, opt_compareFn) { + var compArr = new Array(arr.length); + for (var i = 0; i < arr.length; i++) { + compArr[i] = {index: i, value: arr[i]}; + } + var valueCompareFn = opt_compareFn || goog.array.defaultCompare; + function stableCompareFn(obj1, obj2) { + return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index; + } + goog.array.sort(compArr, stableCompareFn); + for (var i = 0; i < arr.length; i++) { + arr[i] = compArr[i].value; + } +}; + + +/** + * Sort the specified array into ascending order based on item keys + * returned by the specified key function. + * If no opt_compareFn is specified, the keys are compared in ascending order + * using <code>goog.array.defaultCompare</code>. + * + * Runtime: O(S(f(n)), where S is runtime of <code>goog.array.sort</code> + * and f(n) is runtime of the key function. + * + * @param {Array<T>} arr The array to be sorted. + * @param {function(T): K} keyFn Function taking array element and returning + * a key used for sorting this element. + * @param {?function(K, K): number=} opt_compareFn Optional comparison function + * by which the keys are to be ordered. Should take 2 arguments to compare, + * and return a negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template T,K + */ +goog.array.sortByKey = function(arr, keyFn, opt_compareFn) { + var keyCompareFn = opt_compareFn || goog.array.defaultCompare; + goog.array.sort( + arr, function(a, b) { return keyCompareFn(keyFn(a), keyFn(b)); }); +}; + + +/** + * Sorts an array of objects by the specified object key and compare + * function. If no compare function is provided, the key values are + * compared in ascending order using <code>goog.array.defaultCompare</code>. + * This won't work for keys that get renamed by the compiler. So use + * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}. + * @param {Array<Object>} arr An array of objects to sort. + * @param {string} key The object key to sort by. + * @param {Function=} opt_compareFn The function to use to compare key + * values. + */ +goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) { + goog.array.sortByKey(arr, function(obj) { return obj[key]; }, opt_compareFn); +}; + + +/** + * Tells if the array is sorted. + * @param {!Array<T>} arr The array. + * @param {?function(T,T):number=} opt_compareFn Function to compare the + * array elements. + * Should take 2 arguments to compare, and return a negative number, zero, + * or a positive number depending on whether the first argument is less + * than, equal to, or greater than the second. + * @param {boolean=} opt_strict If true no equal elements are allowed. + * @return {boolean} Whether the array is sorted. + * @template T + */ +goog.array.isSorted = function(arr, opt_compareFn, opt_strict) { + var compare = opt_compareFn || goog.array.defaultCompare; + for (var i = 1; i < arr.length; i++) { + var compareResult = compare(arr[i - 1], arr[i]); + if (compareResult > 0 || compareResult == 0 && opt_strict) { + return false; + } + } + return true; +}; + + +/** + * Compares two arrays for equality. Two arrays are considered equal if they + * have the same length and their corresponding elements are equal according to + * the comparison function. + * + * @param {IArrayLike<?>} arr1 The first array to compare. + * @param {IArrayLike<?>} arr2 The second array to compare. + * @param {Function=} opt_equalsFn Optional comparison function. + * Should take 2 arguments to compare, and return true if the arguments + * are equal. Defaults to {@link goog.array.defaultCompareEquality} which + * compares the elements using the built-in '===' operator. + * @return {boolean} Whether the two arrays are equal. + */ +goog.array.equals = function(arr1, arr2, opt_equalsFn) { + if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) || + arr1.length != arr2.length) { + return false; + } + var l = arr1.length; + var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality; + for (var i = 0; i < l; i++) { + if (!equalsFn(arr1[i], arr2[i])) { + return false; + } + } + return true; +}; + + +/** + * 3-way array compare function. + * @param {!IArrayLike<VALUE>} arr1 The first array to + * compare. + * @param {!IArrayLike<VALUE>} arr2 The second array to + * compare. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is to be ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {number} Negative number, zero, or a positive number depending on + * whether the first argument is less than, equal to, or greater than the + * second. + * @template VALUE + */ +goog.array.compare3 = function(arr1, arr2, opt_compareFn) { + var compare = opt_compareFn || goog.array.defaultCompare; + var l = Math.min(arr1.length, arr2.length); + for (var i = 0; i < l; i++) { + var result = compare(arr1[i], arr2[i]); + if (result != 0) { + return result; + } + } + return goog.array.defaultCompare(arr1.length, arr2.length); +}; + + +/** + * Compares its two arguments for order, using the built in < and > + * operators. + * @param {VALUE} a The first object to be compared. + * @param {VALUE} b The second object to be compared. + * @return {number} A negative number, zero, or a positive number as the first + * argument is less than, equal to, or greater than the second, + * respectively. + * @template VALUE + */ +goog.array.defaultCompare = function(a, b) { + return a > b ? 1 : a < b ? -1 : 0; +}; + + +/** + * Compares its two arguments for inverse order, using the built in < and > + * operators. + * @param {VALUE} a The first object to be compared. + * @param {VALUE} b The second object to be compared. + * @return {number} A negative number, zero, or a positive number as the first + * argument is greater than, equal to, or less than the second, + * respectively. + * @template VALUE + */ +goog.array.inverseDefaultCompare = function(a, b) { + return -goog.array.defaultCompare(a, b); +}; + + +/** + * Compares its two arguments for equality, using the built in === operator. + * @param {*} a The first object to compare. + * @param {*} b The second object to compare. + * @return {boolean} True if the two arguments are equal, false otherwise. + */ +goog.array.defaultCompareEquality = function(a, b) { + return a === b; +}; + + +/** + * Inserts a value into a sorted array. The array is not modified if the + * value is already present. + * @param {IArrayLike<VALUE>} array The array to modify. + * @param {VALUE} value The object to insert. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {boolean} True if an element was inserted. + * @template VALUE + */ +goog.array.binaryInsert = function(array, value, opt_compareFn) { + var index = goog.array.binarySearch(array, value, opt_compareFn); + if (index < 0) { + goog.array.insertAt(array, value, -(index + 1)); + return true; + } + return false; +}; + + +/** + * Removes a value from a sorted array. + * @param {!IArrayLike<VALUE>} array The array to modify. + * @param {VALUE} value The object to remove. + * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison + * function by which the array is ordered. Should take 2 arguments to + * compare, and return a negative number, zero, or a positive number + * depending on whether the first argument is less than, equal to, or + * greater than the second. + * @return {boolean} True if an element was removed. + * @template VALUE + */ +goog.array.binaryRemove = function(array, value, opt_compareFn) { + var index = goog.array.binarySearch(array, value, opt_compareFn); + return (index >= 0) ? goog.array.removeAt(array, index) : false; +}; + + +/** + * Splits an array into disjoint buckets according to a splitting function. + * @param {Array<T>} array The array. + * @param {function(this:S, T,number,Array<T>):?} sorter Function to call for + * every element. This takes 3 arguments (the element, the index and the + * array) and must return a valid object key (a string, number, etc), or + * undefined, if that object should not be placed in a bucket. + * @param {S=} opt_obj The object to be used as the value of 'this' within + * sorter. + * @return {!Object} An object, with keys being all of the unique return values + * of sorter, and values being arrays containing the items for + * which the splitter returned that key. + * @template T,S + */ +goog.array.bucket = function(array, sorter, opt_obj) { + var buckets = {}; + + for (var i = 0; i < array.length; i++) { + var value = array[i]; + var key = sorter.call(/** @type {?} */ (opt_obj), value, i, array); + if (goog.isDef(key)) { + // Push the value to the right bucket, creating it if necessary. + var bucket = buckets[key] || (buckets[key] = []); + bucket.push(value); + } + } + + return buckets; +}; + + +/** + * Creates a new object built from the provided array and the key-generation + * function. + * @param {IArrayLike<T>} arr Array or array like object over + * which to iterate whose elements will be the values in the new object. + * @param {?function(this:S, T, number, ?) : string} keyFunc The function to + * call for every element. This function takes 3 arguments (the element, the + * index and the array) and should return a string that will be used as the + * key for the element in the new object. If the function returns the same + * key for more than one element, the value for that key is + * implementation-defined. + * @param {S=} opt_obj The object to be used as the value of 'this' + * within keyFunc. + * @return {!Object<T>} The new object. + * @template T,S + */ +goog.array.toObject = function(arr, keyFunc, opt_obj) { + var ret = {}; + goog.array.forEach(arr, function(element, index) { + ret[keyFunc.call(/** @type {?} */ (opt_obj), element, index, arr)] = + element; + }); + return ret; +}; + + +/** + * Creates a range of numbers in an arithmetic progression. + * + * Range takes 1, 2, or 3 arguments: + * <pre> + * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4] + * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4] + * range(-2, -5, -1) produces [-2, -3, -4] + * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5. + * </pre> + * + * @param {number} startOrEnd The starting value of the range if an end argument + * is provided. Otherwise, the start value is 0, and this is the end value. + * @param {number=} opt_end The optional end value of the range. + * @param {number=} opt_step The step size between range values. Defaults to 1 + * if opt_step is undefined or 0. + * @return {!Array<number>} An array of numbers for the requested range. May be + * an empty array if adding the step would not converge toward the end + * value. + */ +goog.array.range = function(startOrEnd, opt_end, opt_step) { + var array = []; + var start = 0; + var end = startOrEnd; + var step = opt_step || 1; + if (opt_end !== undefined) { + start = startOrEnd; + end = opt_end; + } + + if (step * (end - start) < 0) { + // Sign mismatch: start + step will never reach the end value. + return []; + } + + if (step > 0) { + for (var i = start; i < end; i += step) { + array.push(i); + } + } else { + for (var i = start; i > end; i += step) { + array.push(i); + } + } + return array; +}; + + +/** + * Returns an array consisting of the given value repeated N times. + * + * @param {VALUE} value The value to repeat. + * @param {number} n The repeat count. + * @return {!Array<VALUE>} An array with the repeated value. + * @template VALUE + */ +goog.array.repeat = function(value, n) { + var array = []; + for (var i = 0; i < n; i++) { + array[i] = value; + } + return array; +}; + + +/** + * Returns an array consisting of every argument with all arrays + * expanded in-place recursively. + * + * @param {...*} var_args The values to flatten. + * @return {!Array<?>} An array containing the flattened values. + */ +goog.array.flatten = function(var_args) { + var CHUNK_SIZE = 8192; + + var result = []; + for (var i = 0; i < arguments.length; i++) { + var element = arguments[i]; + if (goog.isArray(element)) { + for (var c = 0; c < element.length; c += CHUNK_SIZE) { + var chunk = goog.array.slice(element, c, c + CHUNK_SIZE); + var recurseResult = goog.array.flatten.apply(null, chunk); + for (var r = 0; r < recurseResult.length; r++) { + result.push(recurseResult[r]); + } + } + } else { + result.push(element); + } + } + return result; +}; + + +/** + * Rotates an array in-place. After calling this method, the element at + * index i will be the element previously at index (i - n) % + * array.length, for all values of i between 0 and array.length - 1, + * inclusive. + * + * For example, suppose list comprises [t, a, n, k, s]. After invoking + * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k]. + * + * @param {!Array<T>} array The array to rotate. + * @param {number} n The amount to rotate. + * @return {!Array<T>} The array. + * @template T + */ +goog.array.rotate = function(array, n) { + goog.asserts.assert(array.length != null); + + if (array.length) { + n %= array.length; + if (n > 0) { + Array.prototype.unshift.apply(array, array.splice(-n, n)); + } else if (n < 0) { + Array.prototype.push.apply(array, array.splice(0, -n)); + } + } + return array; +}; + + +/** + * Moves one item of an array to a new position keeping the order of the rest + * of the items. Example use case: keeping a list of JavaScript objects + * synchronized with the corresponding list of DOM elements after one of the + * elements has been dragged to a new position. + * @param {!IArrayLike<?>} arr The array to modify. + * @param {number} fromIndex Index of the item to move between 0 and + * {@code arr.length - 1}. + * @param {number} toIndex Target index between 0 and {@code arr.length - 1}. + */ +goog.array.moveItem = function(arr, fromIndex, toIndex) { + goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length); + goog.asserts.assert(toIndex >= 0 && toIndex < arr.length); + // Remove 1 item at fromIndex. + var removedItems = Array.prototype.splice.call(arr, fromIndex, 1); + // Insert the removed item at toIndex. + Array.prototype.splice.call(arr, toIndex, 0, removedItems[0]); + // We don't use goog.array.insertAt and goog.array.removeAt, because they're + // significantly slower than splice. +}; + + +/** + * Creates a new array for which the element at position i is an array of the + * ith element of the provided arrays. The returned array will only be as long + * as the shortest array provided; additional values are ignored. For example, + * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]]. + * + * This is similar to the zip() function in Python. See {@link + * http://docs.python.org/library/functions.html#zip} + * + * @param {...!IArrayLike<?>} var_args Arrays to be combined. + * @return {!Array<!Array<?>>} A new array of arrays created from + * provided arrays. + */ +goog.array.zip = function(var_args) { + if (!arguments.length) { + return []; + } + var result = []; + var minLen = arguments[0].length; + for (var i = 1; i < arguments.length; i++) { + if (arguments[i].length < minLen) { + minLen = arguments[i].length; + } + } + for (var i = 0; i < minLen; i++) { + var value = []; + for (var j = 0; j < arguments.length; j++) { + value.push(arguments[j][i]); + } + result.push(value); + } + return result; +}; + + +/** + * Shuffles the values in the specified array using the Fisher-Yates in-place + * shuffle (also known as the Knuth Shuffle). By default, calls Math.random() + * and so resets the state of that random number generator. Similarly, may reset + * the state of the any other specified random number generator. + * + * Runtime: O(n) + * + * @param {!Array<?>} arr The array to be shuffled. + * @param {function():number=} opt_randFn Optional random function to use for + * shuffling. + * Takes no arguments, and returns a random number on the interval [0, 1). + * Defaults to Math.random() using JavaScript's built-in Math library. + */ +goog.array.shuffle = function(arr, opt_randFn) { + var randFn = opt_randFn || Math.random; + + for (var i = arr.length - 1; i > 0; i--) { + // Choose a random array index in [0, i] (inclusive with i). + var j = Math.floor(randFn() * (i + 1)); + + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } +}; + + +/** + * Returns a new array of elements from arr, based on the indexes of elements + * provided by index_arr. For example, the result of index copying + * ['a', 'b', 'c'] with index_arr [1,0,0,2] is ['b', 'a', 'a', 'c']. + * + * @param {!Array<T>} arr The array to get a indexed copy from. + * @param {!Array<number>} index_arr An array of indexes to get from arr. + * @return {!Array<T>} A new array of elements from arr in index_arr order. + * @template T + */ +goog.array.copyByIndex = function(arr, index_arr) { + var result = []; + goog.array.forEach(index_arr, function(index) { result.push(arr[index]); }); + return result; +}; + + +/** + * Maps each element of the input array into zero or more elements of the output + * array. + * + * @param {!IArrayLike<VALUE>|string} arr Array or array like object + * over which to iterate. + * @param {function(this:THIS, VALUE, number, ?): !Array<RESULT>} f The function + * to call for every element. This function takes 3 arguments (the element, + * the index and the array) and should return an array. The result will be + * used to extend a new array. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within f. + * @return {!Array<RESULT>} a new array with the concatenation of all arrays + * returned from f. + * @template THIS, VALUE, RESULT + */ +goog.array.concatMap = function(arr, f, opt_obj) { + return goog.array.concat.apply([], goog.array.map(arr, f, opt_obj)); +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Additional mathematical functions. + */ + +goog.provide('goog.math'); + +goog.require('goog.array'); +goog.require('goog.asserts'); + + +/** + * Returns a random integer greater than or equal to 0 and less than {@code a}. + * @param {number} a The upper bound for the random integer (exclusive). + * @return {number} A random integer N such that 0 <= N < a. + */ +goog.math.randomInt = function(a) { + return Math.floor(Math.random() * a); +}; + + +/** + * Returns a random number greater than or equal to {@code a} and less than + * {@code b}. + * @param {number} a The lower bound for the random number (inclusive). + * @param {number} b The upper bound for the random number (exclusive). + * @return {number} A random number N such that a <= N < b. + */ +goog.math.uniformRandom = function(a, b) { + return a + Math.random() * (b - a); +}; + + +/** + * Takes a number and clamps it to within the provided bounds. + * @param {number} value The input number. + * @param {number} min The minimum value to return. + * @param {number} max The maximum value to return. + * @return {number} The input number if it is within bounds, or the nearest + * number within the bounds. + */ +goog.math.clamp = function(value, min, max) { + return Math.min(Math.max(value, min), max); +}; + + +/** + * The % operator in JavaScript returns the remainder of a / b, but differs from + * some other languages in that the result will have the same sign as the + * dividend. For example, -1 % 8 == -1, whereas in some other languages + * (such as Python) the result would be 7. This function emulates the more + * correct modulo behavior, which is useful for certain applications such as + * calculating an offset index in a circular list. + * + * @param {number} a The dividend. + * @param {number} b The divisor. + * @return {number} a % b where the result is between 0 and b (either 0 <= x < b + * or b < x <= 0, depending on the sign of b). + */ +goog.math.modulo = function(a, b) { + var r = a % b; + // If r and b differ in sign, add b to wrap the result to the correct sign. + return (r * b < 0) ? r + b : r; +}; + + +/** + * Performs linear interpolation between values a and b. Returns the value + * between a and b proportional to x (when x is between 0 and 1. When x is + * outside this range, the return value is a linear extrapolation). + * @param {number} a A number. + * @param {number} b A number. + * @param {number} x The proportion between a and b. + * @return {number} The interpolated value between a and b. + */ +goog.math.lerp = function(a, b, x) { + return a + x * (b - a); +}; + + +/** + * Tests whether the two values are equal to each other, within a certain + * tolerance to adjust for floating point errors. + * @param {number} a A number. + * @param {number} b A number. + * @param {number=} opt_tolerance Optional tolerance range. Defaults + * to 0.000001. If specified, should be greater than 0. + * @return {boolean} Whether {@code a} and {@code b} are nearly equal. + */ +goog.math.nearlyEquals = function(a, b, opt_tolerance) { + return Math.abs(a - b) <= (opt_tolerance || 0.000001); +}; + + +// TODO(user): Rename to normalizeAngle, retaining old name as deprecated +// alias. +/** + * Normalizes an angle to be in range [0-360). Angles outside this range will + * be normalized to be the equivalent angle with that range. + * @param {number} angle Angle in degrees. + * @return {number} Standardized angle. + */ +goog.math.standardAngle = function(angle) { + return goog.math.modulo(angle, 360); +}; + + +/** + * Normalizes an angle to be in range [0-2*PI). Angles outside this range will + * be normalized to be the equivalent angle with that range. + * @param {number} angle Angle in radians. + * @return {number} Standardized angle. + */ +goog.math.standardAngleInRadians = function(angle) { + return goog.math.modulo(angle, 2 * Math.PI); +}; + + +/** + * Converts degrees to radians. + * @param {number} angleDegrees Angle in degrees. + * @return {number} Angle in radians. + */ +goog.math.toRadians = function(angleDegrees) { + return angleDegrees * Math.PI / 180; +}; + + +/** + * Converts radians to degrees. + * @param {number} angleRadians Angle in radians. + * @return {number} Angle in degrees. + */ +goog.math.toDegrees = function(angleRadians) { + return angleRadians * 180 / Math.PI; +}; + + +/** + * For a given angle and radius, finds the X portion of the offset. + * @param {number} degrees Angle in degrees (zero points in +X direction). + * @param {number} radius Radius. + * @return {number} The x-distance for the angle and radius. + */ +goog.math.angleDx = function(degrees, radius) { + return radius * Math.cos(goog.math.toRadians(degrees)); +}; + + +/** + * For a given angle and radius, finds the Y portion of the offset. + * @param {number} degrees Angle in degrees (zero points in +X direction). + * @param {number} radius Radius. + * @return {number} The y-distance for the angle and radius. + */ +goog.math.angleDy = function(degrees, radius) { + return radius * Math.sin(goog.math.toRadians(degrees)); +}; + + +/** + * Computes the angle between two points (x1,y1) and (x2,y2). + * Angle zero points in the +X direction, 90 degrees points in the +Y + * direction (down) and from there we grow clockwise towards 360 degrees. + * @param {number} x1 x of first point. + * @param {number} y1 y of first point. + * @param {number} x2 x of second point. + * @param {number} y2 y of second point. + * @return {number} Standardized angle in degrees of the vector from + * x1,y1 to x2,y2. + */ +goog.math.angle = function(x1, y1, x2, y2) { + return goog.math.standardAngle( + goog.math.toDegrees(Math.atan2(y2 - y1, x2 - x1))); +}; + + +/** + * Computes the difference between startAngle and endAngle (angles in degrees). + * @param {number} startAngle Start angle in degrees. + * @param {number} endAngle End angle in degrees. + * @return {number} The number of degrees that when added to + * startAngle will result in endAngle. Positive numbers mean that the + * direction is clockwise. Negative numbers indicate a counter-clockwise + * direction. + * The shortest route (clockwise vs counter-clockwise) between the angles + * is used. + * When the difference is 180 degrees, the function returns 180 (not -180) + * angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10. + * angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20. + */ +goog.math.angleDifference = function(startAngle, endAngle) { + var d = + goog.math.standardAngle(endAngle) - goog.math.standardAngle(startAngle); + if (d > 180) { + d = d - 360; + } else if (d <= -180) { + d = 360 + d; + } + return d; +}; + + +/** + * Returns the sign of a number as per the "sign" or "signum" function. + * @param {number} x The number to take the sign of. + * @return {number} -1 when negative, 1 when positive, 0 when 0. Preserves + * signed zeros and NaN. + */ +goog.math.sign = Math.sign || function(x) { + if (x > 0) { + return 1; + } + if (x < 0) { + return -1; + } + return x; // Preserves signed zeros and NaN. +}; + + +/** + * JavaScript implementation of Longest Common Subsequence problem. + * http://en.wikipedia.org/wiki/Longest_common_subsequence + * + * Returns the longest possible array that is subarray of both of given arrays. + * + * @param {IArrayLike<S>} array1 First array of objects. + * @param {IArrayLike<T>} array2 Second array of objects. + * @param {Function=} opt_compareFn Function that acts as a custom comparator + * for the array ojects. Function should return true if objects are equal, + * otherwise false. + * @param {Function=} opt_collectorFn Function used to decide what to return + * as a result subsequence. It accepts 2 arguments: index of common element + * in the first array and index in the second. The default function returns + * element from the first array. + * @return {!Array<S|T>} A list of objects that are common to both arrays + * such that there is no common subsequence with size greater than the + * length of the list. + * @template S,T + */ +goog.math.longestCommonSubsequence = function( + array1, array2, opt_compareFn, opt_collectorFn) { + + var compare = opt_compareFn || function(a, b) { return a == b; }; + + var collect = opt_collectorFn || function(i1, i2) { return array1[i1]; }; + + var length1 = array1.length; + var length2 = array2.length; + + var arr = []; + for (var i = 0; i < length1 + 1; i++) { + arr[i] = []; + arr[i][0] = 0; + } + + for (var j = 0; j < length2 + 1; j++) { + arr[0][j] = 0; + } + + for (i = 1; i <= length1; i++) { + for (j = 1; j <= length2; j++) { + if (compare(array1[i - 1], array2[j - 1])) { + arr[i][j] = arr[i - 1][j - 1] + 1; + } else { + arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]); + } + } + } + + // Backtracking + var result = []; + var i = length1, j = length2; + while (i > 0 && j > 0) { + if (compare(array1[i - 1], array2[j - 1])) { + result.unshift(collect(i - 1, j - 1)); + i--; + j--; + } else { + if (arr[i - 1][j] > arr[i][j - 1]) { + i--; + } else { + j--; + } + } + } + + return result; +}; + + +/** + * Returns the sum of the arguments. + * @param {...number} var_args Numbers to add. + * @return {number} The sum of the arguments (0 if no arguments were provided, + * {@code NaN} if any of the arguments is not a valid number). + */ +goog.math.sum = function(var_args) { + return /** @type {number} */ ( + goog.array.reduce( + arguments, function(sum, value) { return sum + value; }, 0)); +}; + + +/** + * Returns the arithmetic mean of the arguments. + * @param {...number} var_args Numbers to average. + * @return {number} The average of the arguments ({@code NaN} if no arguments + * were provided or any of the arguments is not a valid number). + */ +goog.math.average = function(var_args) { + return goog.math.sum.apply(null, arguments) / arguments.length; +}; + + +/** + * Returns the unbiased sample variance of the arguments. For a definition, + * see e.g. http://en.wikipedia.org/wiki/Variance + * @param {...number} var_args Number samples to analyze. + * @return {number} The unbiased sample variance of the arguments (0 if fewer + * than two samples were provided, or {@code NaN} if any of the samples is + * not a valid number). + */ +goog.math.sampleVariance = function(var_args) { + var sampleSize = arguments.length; + if (sampleSize < 2) { + return 0; + } + + var mean = goog.math.average.apply(null, arguments); + var variance = + goog.math.sum.apply(null, goog.array.map(arguments, function(val) { + return Math.pow(val - mean, 2); + })) / (sampleSize - 1); + + return variance; +}; + + +/** + * Returns the sample standard deviation of the arguments. For a definition of + * sample standard deviation, see e.g. + * http://en.wikipedia.org/wiki/Standard_deviation + * @param {...number} var_args Number samples to analyze. + * @return {number} The sample standard deviation of the arguments (0 if fewer + * than two samples were provided, or {@code NaN} if any of the samples is + * not a valid number). + */ +goog.math.standardDeviation = function(var_args) { + return Math.sqrt(goog.math.sampleVariance.apply(null, arguments)); +}; + + +/** + * Returns whether the supplied number represents an integer, i.e. that is has + * no fractional component. No range-checking is performed on the number. + * @param {number} num The number to test. + * @return {boolean} Whether {@code num} is an integer. + */ +goog.math.isInt = function(num) { + return isFinite(num) && num % 1 == 0; +}; + + +/** + * Returns whether the supplied number is finite and not NaN. + * @param {number} num The number to test. + * @return {boolean} Whether {@code num} is a finite number. + */ +goog.math.isFiniteNumber = function(num) { + return isFinite(num) && !isNaN(num); +}; + + +/** + * @param {number} num The number to test. + * @return {boolean} Whether it is negative zero. + */ +goog.math.isNegativeZero = function(num) { + return num == 0 && 1 / num < 0; +}; + + +/** + * Returns the precise value of floor(log10(num)). + * Simpler implementations didn't work because of floating point rounding + * errors. For example + * <ul> + * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3. + * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15. + * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1. + * </ul> + * @param {number} num A floating point number. + * @return {number} Its logarithm to base 10 rounded down to the nearest + * integer if num > 0. -Infinity if num == 0. NaN if num < 0. + */ +goog.math.log10Floor = function(num) { + if (num > 0) { + var x = Math.round(Math.log(num) * Math.LOG10E); + return x - (parseFloat('1e' + x) > num ? 1 : 0); + } + return num == 0 ? -Infinity : NaN; +}; + + +/** + * A tweaked variant of {@code Math.floor} which tolerates if the passed number + * is infinitesimally smaller than the closest integer. It often happens with + * the results of floating point calculations because of the finite precision + * of the intermediate results. For example {@code Math.floor(Math.log(1000) / + * Math.LN10) == 2}, not 3 as one would expect. + * @param {number} num A number. + * @param {number=} opt_epsilon An infinitesimally small positive number, the + * rounding error to tolerate. + * @return {number} The largest integer less than or equal to {@code num}. + */ +goog.math.safeFloor = function(num, opt_epsilon) { + goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0); + return Math.floor(num + (opt_epsilon || 2e-15)); +}; + + +/** + * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for + * details. + * @param {number} num A number. + * @param {number=} opt_epsilon An infinitesimally small positive number, the + * rounding error to tolerate. + * @return {number} The smallest integer greater than or equal to {@code num}. + */ +goog.math.safeCeil = function(num, opt_epsilon) { + goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0); + return Math.ceil(num - (opt_epsilon || 2e-15)); +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities related to color and color conversion. + */ + +goog.provide('goog.color'); +goog.provide('goog.color.Hsl'); +goog.provide('goog.color.Hsv'); +goog.provide('goog.color.Rgb'); + +goog.require('goog.color.names'); +goog.require('goog.math'); + + +/** + * RGB color representation. An array containing three elements [r, g, b], + * each an integer in [0, 255], representing the red, green, and blue components + * of the color respectively. + * @typedef {Array<number>} + */ +goog.color.Rgb; + + +/** + * HSV color representation. An array containing three elements [h, s, v]: + * h (hue) must be an integer in [0, 360], cyclic. + * s (saturation) must be a number in [0, 1]. + * v (value/brightness) must be an integer in [0, 255]. + * @typedef {Array<number>} + */ +goog.color.Hsv; + + +/** + * HSL color representation. An array containing three elements [h, s, l]: + * h (hue) must be an integer in [0, 360], cyclic. + * s (saturation) must be a number in [0, 1]. + * l (lightness) must be a number in [0, 1]. + * @typedef {Array<number>} + */ +goog.color.Hsl; + + +/** + * Parses a color out of a string. + * @param {string} str Color in some format. + * @return {{hex: string, type: string}} 'hex' is a string containing a hex + * representation of the color, 'type' is a string containing the type + * of color format passed in ('hex', 'rgb', 'named'). + */ +goog.color.parse = function(str) { + var result = {}; + str = String(str); + + var maybeHex = goog.color.prependHashIfNecessaryHelper(str); + if (goog.color.isValidHexColor_(maybeHex)) { + result.hex = goog.color.normalizeHex(maybeHex); + result.type = 'hex'; + return result; + } else { + var rgb = goog.color.isValidRgbColor_(str); + if (rgb.length) { + result.hex = goog.color.rgbArrayToHex(rgb); + result.type = 'rgb'; + return result; + } else if (goog.color.names) { + var hex = goog.color.names[str.toLowerCase()]; + if (hex) { + result.hex = hex; + result.type = 'named'; + return result; + } + } + } + throw Error(str + ' is not a valid color string'); +}; + + +/** + * Determines if the given string can be parsed as a color. + * {@see goog.color.parse}. + * @param {string} str Potential color string. + * @return {boolean} True if str is in a format that can be parsed to a color. + */ +goog.color.isValidColor = function(str) { + var maybeHex = goog.color.prependHashIfNecessaryHelper(str); + return !!( + goog.color.isValidHexColor_(maybeHex) || + goog.color.isValidRgbColor_(str).length || + goog.color.names && goog.color.names[str.toLowerCase()]); +}; + + +/** + * Parses red, green, blue components out of a valid rgb color string. + * Throws Error if the color string is invalid. + * @param {string} str RGB representation of a color. + * {@see goog.color.isValidRgbColor_}. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.parseRgb = function(str) { + var rgb = goog.color.isValidRgbColor_(str); + if (!rgb.length) { + throw Error(str + ' is not a valid RGB color'); + } + return rgb; +}; + + +/** + * Converts a hex representation of a color to RGB. + * @param {string} hexColor Color to convert. + * @return {string} string of the form 'rgb(R,G,B)' which can be used in + * styles. + */ +goog.color.hexToRgbStyle = function(hexColor) { + return goog.color.rgbStyle_(goog.color.hexToRgb(hexColor)); +}; + + +/** + * Regular expression for extracting the digits in a hex color triplet. + * @type {RegExp} + * @private + */ +goog.color.hexTripletRe_ = /#(.)(.)(.)/; + + +/** + * Normalize an hex representation of a color + * @param {string} hexColor an hex color string. + * @return {string} hex color in the format '#rrggbb' with all lowercase + * literals. + */ +goog.color.normalizeHex = function(hexColor) { + if (!goog.color.isValidHexColor_(hexColor)) { + throw Error("'" + hexColor + "' is not a valid hex color"); + } + if (hexColor.length == 4) { // of the form #RGB + hexColor = hexColor.replace(goog.color.hexTripletRe_, '#$1$1$2$2$3$3'); + } + return hexColor.toLowerCase(); +}; + + +/** + * Converts a hex representation of a color to RGB. + * @param {string} hexColor Color to convert. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hexToRgb = function(hexColor) { + hexColor = goog.color.normalizeHex(hexColor); + var r = parseInt(hexColor.substr(1, 2), 16); + var g = parseInt(hexColor.substr(3, 2), 16); + var b = parseInt(hexColor.substr(5, 2), 16); + + return [r, g, b]; +}; + + +/** + * Converts a color from RGB to hex representation. + * @param {number} r Amount of red, int between 0 and 255. + * @param {number} g Amount of green, int between 0 and 255. + * @param {number} b Amount of blue, int between 0 and 255. + * @return {string} hex representation of the color. + */ +goog.color.rgbToHex = function(r, g, b) { + r = Number(r); + g = Number(g); + b = Number(b); + if (r != (r & 255) || g != (g & 255) || b != (b & 255)) { + throw Error('"(' + r + ',' + g + ',' + b + '") is not a valid RGB color'); + } + var hexR = goog.color.prependZeroIfNecessaryHelper(r.toString(16)); + var hexG = goog.color.prependZeroIfNecessaryHelper(g.toString(16)); + var hexB = goog.color.prependZeroIfNecessaryHelper(b.toString(16)); + return '#' + hexR + hexG + hexB; +}; + + +/** + * Converts a color from RGB to hex representation. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {string} hex representation of the color. + */ +goog.color.rgbArrayToHex = function(rgb) { + return goog.color.rgbToHex(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Converts a color from RGB color space to HSL color space. + * Modified from {@link http://en.wikipedia.org/wiki/HLS_color_space}. + * @param {number} r Value of red, in [0, 255]. + * @param {number} g Value of green, in [0, 255]. + * @param {number} b Value of blue, in [0, 255]. + * @return {!goog.color.Hsl} hsl representation of the color. + */ +goog.color.rgbToHsl = function(r, g, b) { + // First must normalize r, g, b to be between 0 and 1. + var normR = r / 255; + var normG = g / 255; + var normB = b / 255; + var max = Math.max(normR, normG, normB); + var min = Math.min(normR, normG, normB); + var h = 0; + var s = 0; + + // Luminosity is the average of the max and min rgb color intensities. + var l = 0.5 * (max + min); + + // The hue and saturation are dependent on which color intensity is the max. + // If max and min are equal, the color is gray and h and s should be 0. + if (max != min) { + if (max == normR) { + h = 60 * (normG - normB) / (max - min); + } else if (max == normG) { + h = 60 * (normB - normR) / (max - min) + 120; + } else if (max == normB) { + h = 60 * (normR - normG) / (max - min) + 240; + } + + if (0 < l && l <= 0.5) { + s = (max - min) / (2 * l); + } else { + s = (max - min) / (2 - 2 * l); + } + } + + // Make sure the hue falls between 0 and 360. + return [Math.round(h + 360) % 360, s, l]; +}; + + +/** + * Converts a color from RGB color space to HSL color space. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {!goog.color.Hsl} hsl representation of the color. + */ +goog.color.rgbArrayToHsl = function(rgb) { + return goog.color.rgbToHsl(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Helper for hslToRgb. + * @param {number} v1 Helper variable 1. + * @param {number} v2 Helper variable 2. + * @param {number} vH Helper variable 3. + * @return {number} Appropriate RGB value, given the above. + * @private + */ +goog.color.hueToRgb_ = function(v1, v2, vH) { + if (vH < 0) { + vH += 1; + } else if (vH > 1) { + vH -= 1; + } + if ((6 * vH) < 1) { + return (v1 + (v2 - v1) * 6 * vH); + } else if (2 * vH < 1) { + return v2; + } else if (3 * vH < 2) { + return (v1 + (v2 - v1) * ((2 / 3) - vH) * 6); + } + return v1; +}; + + +/** + * Converts a color from HSL color space to RGB color space. + * Modified from {@link http://www.easyrgb.com/math.html} + * @param {number} h Hue, in [0, 360]. + * @param {number} s Saturation, in [0, 1]. + * @param {number} l Luminosity, in [0, 1]. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hslToRgb = function(h, s, l) { + var r = 0; + var g = 0; + var b = 0; + var normH = h / 360; // normalize h to fall in [0, 1] + + if (s == 0) { + r = g = b = l * 255; + } else { + var temp1 = 0; + var temp2 = 0; + if (l < 0.5) { + temp2 = l * (1 + s); + } else { + temp2 = l + s - (s * l); + } + temp1 = 2 * l - temp2; + r = 255 * goog.color.hueToRgb_(temp1, temp2, normH + (1 / 3)); + g = 255 * goog.color.hueToRgb_(temp1, temp2, normH); + b = 255 * goog.color.hueToRgb_(temp1, temp2, normH - (1 / 3)); + } + + return [Math.round(r), Math.round(g), Math.round(b)]; +}; + + +/** + * Converts a color from HSL color space to RGB color space. + * @param {goog.color.Hsl} hsl hsl representation of the color. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hslArrayToRgb = function(hsl) { + return goog.color.hslToRgb(hsl[0], hsl[1], hsl[2]); +}; + + +/** + * Helper for isValidHexColor_. + * @type {RegExp} + * @private + */ +goog.color.validHexColorRe_ = /^#(?:[0-9a-f]{3}){1,2}$/i; + + +/** + * Checks if a string is a valid hex color. We expect strings of the format + * #RRGGBB (ex: #1b3d5f) or #RGB (ex: #3CA == #33CCAA). + * @param {string} str String to check. + * @return {boolean} Whether the string is a valid hex color. + * @private + */ +goog.color.isValidHexColor_ = function(str) { + return goog.color.validHexColorRe_.test(str); +}; + + +/** + * Helper for isNormalizedHexColor_. + * @type {RegExp} + * @private + */ +goog.color.normalizedHexColorRe_ = /^#[0-9a-f]{6}$/; + + +/** + * Checks if a string is a normalized hex color. + * We expect strings of the format #RRGGBB (ex: #1b3d5f) + * using only lowercase letters. + * @param {string} str String to check. + * @return {boolean} Whether the string is a normalized hex color. + * @private + */ +goog.color.isNormalizedHexColor_ = function(str) { + return goog.color.normalizedHexColorRe_.test(str); +}; + + +/** + * Regular expression for matching and capturing RGB style strings. Helper for + * isValidRgbColor_. + * @type {RegExp} + * @private + */ +goog.color.rgbColorRe_ = + /^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i; + + +/** + * Checks if a string is a valid rgb color. We expect strings of the format + * '(r, g, b)', or 'rgb(r, g, b)', where each color component is an int in + * [0, 255]. + * @param {string} str String to check. + * @return {!goog.color.Rgb} the rgb representation of the color if it is + * a valid color, or the empty array otherwise. + * @private + */ +goog.color.isValidRgbColor_ = function(str) { + // Each component is separate (rather than using a repeater) so we can + // capture the match. Also, we explicitly set each component to be either 0, + // or start with a non-zero, to prevent octal numbers from slipping through. + var regExpResultArray = str.match(goog.color.rgbColorRe_); + if (regExpResultArray) { + var r = Number(regExpResultArray[1]); + var g = Number(regExpResultArray[2]); + var b = Number(regExpResultArray[3]); + if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) { + return [r, g, b]; + } + } + return []; +}; + + +/** + * Takes a hex value and prepends a zero if it's a single digit. + * Small helper method for use by goog.color and friends. + * @param {string} hex Hex value to prepend if single digit. + * @return {string} hex value prepended with zero if it was single digit, + * otherwise the same value that was passed in. + */ +goog.color.prependZeroIfNecessaryHelper = function(hex) { + return hex.length == 1 ? '0' + hex : hex; +}; + + +/** + * Takes a string a prepends a '#' sign if one doesn't exist. + * Small helper method for use by goog.color and friends. + * @param {string} str String to check. + * @return {string} The value passed in, prepended with a '#' if it didn't + * already have one. + */ +goog.color.prependHashIfNecessaryHelper = function(str) { + return str.charAt(0) == '#' ? str : '#' + str; +}; + + +/** + * Takes an array of [r, g, b] and converts it into a string appropriate for + * CSS styles. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {string} string of the form 'rgb(r,g,b)'. + * @private + */ +goog.color.rgbStyle_ = function(rgb) { + return 'rgb(' + rgb.join(',') + ')'; +}; + + +/** + * Converts an HSV triplet to an RGB array. V is brightness because b is + * reserved for blue in RGB. + * @param {number} h Hue value in [0, 360]. + * @param {number} s Saturation value in [0, 1]. + * @param {number} brightness brightness in [0, 255]. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hsvToRgb = function(h, s, brightness) { + var red = 0; + var green = 0; + var blue = 0; + if (s == 0) { + red = brightness; + green = brightness; + blue = brightness; + } else { + var sextant = Math.floor(h / 60); + var remainder = (h / 60) - sextant; + var val1 = brightness * (1 - s); + var val2 = brightness * (1 - (s * remainder)); + var val3 = brightness * (1 - (s * (1 - remainder))); + switch (sextant) { + case 1: + red = val2; + green = brightness; + blue = val1; + break; + case 2: + red = val1; + green = brightness; + blue = val3; + break; + case 3: + red = val1; + green = val2; + blue = brightness; + break; + case 4: + red = val3; + green = val1; + blue = brightness; + break; + case 5: + red = brightness; + green = val1; + blue = val2; + break; + case 6: + case 0: + red = brightness; + green = val3; + blue = val1; + break; + } + } + + return [Math.floor(red), Math.floor(green), Math.floor(blue)]; +}; + + +/** + * Converts from RGB values to an array of HSV values. + * @param {number} red Red value in [0, 255]. + * @param {number} green Green value in [0, 255]. + * @param {number} blue Blue value in [0, 255]. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.rgbToHsv = function(red, green, blue) { + + var max = Math.max(Math.max(red, green), blue); + var min = Math.min(Math.min(red, green), blue); + var hue; + var saturation; + var value = max; + if (min == max) { + hue = 0; + saturation = 0; + } else { + var delta = (max - min); + saturation = delta / max; + + if (red == max) { + hue = (green - blue) / delta; + } else if (green == max) { + hue = 2 + ((blue - red) / delta); + } else { + hue = 4 + ((red - green) / delta); + } + hue *= 60; + if (hue < 0) { + hue += 360; + } + if (hue > 360) { + hue -= 360; + } + } + + return [hue, saturation, value]; +}; + + +/** + * Converts from an array of RGB values to an array of HSV values. + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.rgbArrayToHsv = function(rgb) { + return goog.color.rgbToHsv(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Converts an HSV triplet to an RGB array. + * @param {goog.color.Hsv} hsv hsv representation of the color. + * @return {!goog.color.Rgb} rgb representation of the color. + */ +goog.color.hsvArrayToRgb = function(hsv) { + return goog.color.hsvToRgb(hsv[0], hsv[1], hsv[2]); +}; + + +/** + * Converts a hex representation of a color to HSL. + * @param {string} hex Color to convert. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.hexToHsl = function(hex) { + var rgb = goog.color.hexToRgb(hex); + return goog.color.rgbToHsl(rgb[0], rgb[1], rgb[2]); +}; + + +/** + * Converts from h,s,l values to a hex string + * @param {number} h Hue, in [0, 360]. + * @param {number} s Saturation, in [0, 1]. + * @param {number} l Luminosity, in [0, 1]. + * @return {string} hex representation of the color. + */ +goog.color.hslToHex = function(h, s, l) { + return goog.color.rgbArrayToHex(goog.color.hslToRgb(h, s, l)); +}; + + +/** + * Converts from an hsl array to a hex string + * @param {goog.color.Hsl} hsl hsl representation of the color. + * @return {string} hex representation of the color. + */ +goog.color.hslArrayToHex = function(hsl) { + return goog.color.rgbArrayToHex(goog.color.hslToRgb(hsl[0], hsl[1], hsl[2])); +}; + + +/** + * Converts a hex representation of a color to HSV + * @param {string} hex Color to convert. + * @return {!goog.color.Hsv} hsv representation of the color. + */ +goog.color.hexToHsv = function(hex) { + return goog.color.rgbArrayToHsv(goog.color.hexToRgb(hex)); +}; + + +/** + * Converts from h,s,v values to a hex string + * @param {number} h Hue, in [0, 360]. + * @param {number} s Saturation, in [0, 1]. + * @param {number} v Value, in [0, 255]. + * @return {string} hex representation of the color. + */ +goog.color.hsvToHex = function(h, s, v) { + return goog.color.rgbArrayToHex(goog.color.hsvToRgb(h, s, v)); +}; + + +/** + * Converts from an HSV array to a hex string + * @param {goog.color.Hsv} hsv hsv representation of the color. + * @return {string} hex representation of the color. + */ +goog.color.hsvArrayToHex = function(hsv) { + return goog.color.hsvToHex(hsv[0], hsv[1], hsv[2]); +}; + + +/** + * Calculates the Euclidean distance between two color vectors on an HSL sphere. + * A demo of the sphere can be found at: + * http://en.wikipedia.org/wiki/HSL_color_space + * In short, a vector for color (H, S, L) in this system can be expressed as + * (S*L'*cos(2*PI*H), S*L'*sin(2*PI*H), L), where L' = abs(L - 0.5), and we + * simply calculate the 1-2 distance using these coordinates + * @param {goog.color.Hsl} hsl1 First color in hsl representation. + * @param {goog.color.Hsl} hsl2 Second color in hsl representation. + * @return {number} Distance between the two colors, in the range [0, 1]. + */ +goog.color.hslDistance = function(hsl1, hsl2) { + var sl1, sl2; + if (hsl1[2] <= 0.5) { + sl1 = hsl1[1] * hsl1[2]; + } else { + sl1 = hsl1[1] * (1.0 - hsl1[2]); + } + + if (hsl2[2] <= 0.5) { + sl2 = hsl2[1] * hsl2[2]; + } else { + sl2 = hsl2[1] * (1.0 - hsl2[2]); + } + + var h1 = hsl1[0] / 360.0; + var h2 = hsl2[0] / 360.0; + var dh = (h1 - h2) * 2.0 * Math.PI; + return (hsl1[2] - hsl2[2]) * (hsl1[2] - hsl2[2]) + sl1 * sl1 + sl2 * sl2 - + 2 * sl1 * sl2 * Math.cos(dh); +}; + + +/** + * Blend two colors together, using the specified factor to indicate the weight + * given to the first color + * @param {goog.color.Rgb} rgb1 First color represented in rgb. + * @param {goog.color.Rgb} rgb2 Second color represented in rgb. + * @param {number} factor The weight to be given to rgb1 over rgb2. Values + * should be in the range [0, 1]. If less than 0, factor will be set to 0. + * If greater than 1, factor will be set to 1. + * @return {!goog.color.Rgb} Combined color represented in rgb. + */ +goog.color.blend = function(rgb1, rgb2, factor) { + factor = goog.math.clamp(factor, 0, 1); + + return [ + Math.round(factor * rgb1[0] + (1.0 - factor) * rgb2[0]), + Math.round(factor * rgb1[1] + (1.0 - factor) * rgb2[1]), + Math.round(factor * rgb1[2] + (1.0 - factor) * rgb2[2]) + ]; +}; + + +/** + * Adds black to the specified color, darkening it + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @param {number} factor Number in the range [0, 1]. 0 will do nothing, while + * 1 will return black. If less than 0, factor will be set to 0. If greater + * than 1, factor will be set to 1. + * @return {!goog.color.Rgb} Combined rgb color. + */ +goog.color.darken = function(rgb, factor) { + var black = [0, 0, 0]; + return goog.color.blend(black, rgb, factor); +}; + + +/** + * Adds white to the specified color, lightening it + * @param {goog.color.Rgb} rgb rgb representation of the color. + * @param {number} factor Number in the range [0, 1]. 0 will do nothing, while + * 1 will return white. If less than 0, factor will be set to 0. If greater + * than 1, factor will be set to 1. + * @return {!goog.color.Rgb} Combined rgb color. + */ +goog.color.lighten = function(rgb, factor) { + var white = [255, 255, 255]; + return goog.color.blend(white, rgb, factor); +}; + + +/** + * Find the "best" (highest-contrast) of the suggested colors for the prime + * color. Uses W3C formula for judging readability and visual accessibility: + * http://www.w3.org/TR/AERT#color-contrast + * @param {goog.color.Rgb} prime Color represented as a rgb array. + * @param {Array<goog.color.Rgb>} suggestions Array of colors, + * each representing a rgb array. + * @return {!goog.color.Rgb} Highest-contrast color represented by an array.. + */ +goog.color.highContrast = function(prime, suggestions) { + var suggestionsWithDiff = []; + for (var i = 0; i < suggestions.length; i++) { + suggestionsWithDiff.push({ + color: suggestions[i], + diff: goog.color.yiqBrightnessDiff_(suggestions[i], prime) + + goog.color.colorDiff_(suggestions[i], prime) + }); + } + suggestionsWithDiff.sort(function(a, b) { return b.diff - a.diff; }); + return suggestionsWithDiff[0].color; +}; + + +/** + * Calculate brightness of a color according to YIQ formula (brightness is Y). + * More info on YIQ here: http://en.wikipedia.org/wiki/YIQ. Helper method for + * goog.color.highContrast() + * @param {goog.color.Rgb} rgb Color represented by a rgb array. + * @return {number} brightness (Y). + * @private + */ +goog.color.yiqBrightness_ = function(rgb) { + return Math.round((rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000); +}; + + +/** + * Calculate difference in brightness of two colors. Helper method for + * goog.color.highContrast() + * @param {goog.color.Rgb} rgb1 Color represented by a rgb array. + * @param {goog.color.Rgb} rgb2 Color represented by a rgb array. + * @return {number} Brightness difference. + * @private + */ +goog.color.yiqBrightnessDiff_ = function(rgb1, rgb2) { + return Math.abs( + goog.color.yiqBrightness_(rgb1) - goog.color.yiqBrightness_(rgb2)); +}; + + +/** + * Calculate color difference between two colors. Helper method for + * goog.color.highContrast() + * @param {goog.color.Rgb} rgb1 Color represented by a rgb array. + * @param {goog.color.Rgb} rgb2 Color represented by a rgb array. + * @return {number} Color difference. + * @private + */ +goog.color.colorDiff_ = function(rgb1, rgb2) { + return Math.abs(rgb1[0] - rgb2[0]) + Math.abs(rgb1[1] - rgb2[1]) + + Math.abs(rgb1[2] - rgb2[2]); +}; + +// We can't use goog.color or goog.color.alpha because they interally use a hex +// string representation that encodes each channel in a single byte. This +// causes occasional loss of precision and rounding errors, especially in the +// alpha channel. + +goog.provide('ol.color'); + +goog.require('goog.asserts'); +goog.require('goog.color'); +goog.require('goog.color.names'); +goog.require('ol'); +goog.require('ol.math'); + + +/** + * This RegExp matches # followed by 3 or 6 hex digits. + * @const + * @type {RegExp} + * @private + */ +ol.color.hexColorRe_ = /^#(?:[0-9a-f]{3}){1,2}$/i; + + +/** + * @see goog.color.rgbColorRe_ + * @const + * @type {RegExp} + * @private + */ +ol.color.rgbColorRe_ = + /^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i; + + +/** + * @see goog.color.alpha.rgbaColorRe_ + * @const + * @type {RegExp} + * @private + */ +ol.color.rgbaColorRe_ = + /^(?:rgba)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|1|0\.\d{0,10})\)$/i; + + +/** + * Return the color as an array. This function maintains a cache of calculated + * arrays which means the result should not be modified. + * @param {ol.Color|string} color Color. + * @return {ol.Color} Color. + * @api + */ +ol.color.asArray = function(color) { + if (Array.isArray(color)) { + return color; + } else { + goog.asserts.assert(typeof color === 'string', 'Color should be a string'); + return ol.color.fromString(color); + } +}; + + +/** + * Return the color as an rgba string. + * @param {ol.Color|string} color Color. + * @return {string} Rgba string. + * @api + */ +ol.color.asString = function(color) { + if (typeof color === 'string') { + return color; + } else { + goog.asserts.assert(Array.isArray(color), 'Color should be an array'); + return ol.color.toString(color); + } +}; + + +/** + * @param {string} s String. + * @return {ol.Color} Color. + */ +ol.color.fromString = ( + function() { + + // We maintain a small cache of parsed strings. To provide cheap LRU-like + // semantics, whenever the cache grows too large we simply delete an + // arbitrary 25% of the entries. + + /** + * @const + * @type {number} + */ + var MAX_CACHE_SIZE = 1024; + + /** + * @type {Object.<string, ol.Color>} + */ + var cache = {}; + + /** + * @type {number} + */ + var cacheSize = 0; + + return ( + /** + * @param {string} s String. + * @return {ol.Color} Color. + */ + function(s) { + var color; + if (cache.hasOwnProperty(s)) { + color = cache[s]; + } else { + if (cacheSize >= MAX_CACHE_SIZE) { + var i = 0; + var key; + for (key in cache) { + if ((i++ & 3) === 0) { + delete cache[key]; + --cacheSize; + } + } + } + color = ol.color.fromStringInternal_(s); + cache[s] = color; + ++cacheSize; + } + return color; + }); + + })(); + + +/** + * @param {string} s String. + * @private + * @return {ol.Color} Color. + */ +ol.color.fromStringInternal_ = function(s) { + + var isHex = false; + if (ol.ENABLE_NAMED_COLORS && goog.color.names.hasOwnProperty(s)) { + s = goog.color.names[s]; + isHex = true; + } + + var r, g, b, a, color, match; + if (isHex || (match = ol.color.hexColorRe_.exec(s))) { // hex + var n = s.length - 1; // number of hex digits + goog.asserts.assert(n == 3 || n == 6, + 'Color string length should be 3 or 6'); + var d = n == 3 ? 1 : 2; // number of digits per channel + r = parseInt(s.substr(1 + 0 * d, d), 16); + g = parseInt(s.substr(1 + 1 * d, d), 16); + b = parseInt(s.substr(1 + 2 * d, d), 16); + if (d == 1) { + r = (r << 4) + r; + g = (g << 4) + g; + b = (b << 4) + b; + } + a = 1; + color = [r, g, b, a]; + goog.asserts.assert(ol.color.isValid(color), + 'Color should be a valid color'); + return color; + } else if ((match = ol.color.rgbaColorRe_.exec(s))) { // rgba() + r = Number(match[1]); + g = Number(match[2]); + b = Number(match[3]); + a = Number(match[4]); + color = [r, g, b, a]; + return ol.color.normalize(color, color); + } else if ((match = ol.color.rgbColorRe_.exec(s))) { // rgb() + r = Number(match[1]); + g = Number(match[2]); + b = Number(match[3]); + color = [r, g, b, 1]; + return ol.color.normalize(color, color); + } else { + goog.asserts.fail(s + ' is not a valid color'); + } + +}; + + +/** + * @param {ol.Color} color Color. + * @return {boolean} Is valid. + */ +ol.color.isValid = function(color) { + return 0 <= color[0] && color[0] < 256 && + 0 <= color[1] && color[1] < 256 && + 0 <= color[2] && color[2] < 256 && + 0 <= color[3] && color[3] <= 1; +}; + + +/** + * @param {ol.Color} color Color. + * @param {ol.Color=} opt_color Color. + * @return {ol.Color} Clamped color. + */ +ol.color.normalize = function(color, opt_color) { + var result = opt_color || []; + result[0] = ol.math.clamp((color[0] + 0.5) | 0, 0, 255); + result[1] = ol.math.clamp((color[1] + 0.5) | 0, 0, 255); + result[2] = ol.math.clamp((color[2] + 0.5) | 0, 0, 255); + result[3] = ol.math.clamp(color[3], 0, 1); + return result; +}; + + +/** + * @param {ol.Color} color Color. + * @return {string} String. + */ +ol.color.toString = function(color) { + var r = color[0]; + if (r != (r | 0)) { + r = (r + 0.5) | 0; + } + var g = color[1]; + if (g != (g | 0)) { + g = (g + 0.5) | 0; + } + var b = color[2]; + if (b != (b | 0)) { + b = (b + 0.5) | 0; + } + var a = color[3] === undefined ? 1 : color[3]; + return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; +}; + +goog.provide('ol.colorlike'); + +goog.require('ol.color'); + + +/** + * @param {ol.Color|ol.ColorLike} color Color. + * @return {ol.ColorLike} The color as an ol.ColorLike + * @api + */ +ol.colorlike.asColorLike = function(color) { + if (ol.colorlike.isColorLike(color)) { + return /** @type {string|CanvasPattern|CanvasGradient} */ (color); + } else { + return ol.color.asString(/** @type {ol.Color} */ (color)); + } +}; + + +/** + * @param {?} color The value that is potentially an ol.ColorLike + * @return {boolean} Whether the color is an ol.ColorLike + */ +ol.colorlike.isColorLike = function(color) { + return ( + typeof color === 'string' || + color instanceof CanvasPattern || + color instanceof CanvasGradient + ); +}; + +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities used by goog.labs.userAgent tools. These functions + * should not be used outside of goog.labs.userAgent.*. + * + * + * @author nnaze@google.com (Nathan Naze) + */ + +goog.provide('goog.labs.userAgent.util'); + +goog.require('goog.string'); + + +/** + * Gets the native userAgent string from navigator if it exists. + * If navigator or navigator.userAgent string is missing, returns an empty + * string. + * @return {string} + * @private + */ +goog.labs.userAgent.util.getNativeUserAgentString_ = function() { + var navigator = goog.labs.userAgent.util.getNavigator_(); + if (navigator) { + var userAgent = navigator.userAgent; + if (userAgent) { + return userAgent; + } + } + return ''; +}; + + +/** + * Getter for the native navigator. + * This is a separate function so it can be stubbed out in testing. + * @return {Navigator} + * @private + */ +goog.labs.userAgent.util.getNavigator_ = function() { + return goog.global.navigator; +}; + + +/** + * A possible override for applications which wish to not check + * navigator.userAgent but use a specified value for detection instead. + * @private {string} + */ +goog.labs.userAgent.util.userAgent_ = + goog.labs.userAgent.util.getNativeUserAgentString_(); + + +/** + * Applications may override browser detection on the built in + * navigator.userAgent object by setting this string. Set to null to use the + * browser object instead. + * @param {?string=} opt_userAgent The User-Agent override. + */ +goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) { + goog.labs.userAgent.util.userAgent_ = + opt_userAgent || goog.labs.userAgent.util.getNativeUserAgentString_(); +}; + + +/** + * @return {string} The user agent string. + */ +goog.labs.userAgent.util.getUserAgent = function() { + return goog.labs.userAgent.util.userAgent_; +}; + + +/** + * @param {string} str + * @return {boolean} Whether the user agent contains the given string, ignoring + * case. + */ +goog.labs.userAgent.util.matchUserAgent = function(str) { + var userAgent = goog.labs.userAgent.util.getUserAgent(); + return goog.string.contains(userAgent, str); +}; + + +/** + * @param {string} str + * @return {boolean} Whether the user agent contains the given string. + */ +goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) { + var userAgent = goog.labs.userAgent.util.getUserAgent(); + return goog.string.caseInsensitiveContains(userAgent, str); +}; + + +/** + * Parses the user agent into tuples for each section. + * @param {string} userAgent + * @return {!Array<!Array<string>>} Tuples of key, version, and the contents + * of the parenthetical. + */ +goog.labs.userAgent.util.extractVersionTuples = function(userAgent) { + // Matches each section of a user agent string. + // Example UA: + // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us) + // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405 + // This has three version tuples: Mozilla, AppleWebKit, and Mobile. + + var versionRegExp = new RegExp( + // Key. Note that a key may have a space. + // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0') + '(\\w[\\w ]+)' + + + '/' + // slash + '([^\\s]+)' + // version (i.e. '5.0b') + '\\s*' + // whitespace + '(?:\\((.*?)\\))?', // parenthetical info. parentheses not matched. + 'g'); + + var data = []; + var match; + + // Iterate and collect the version tuples. Each iteration will be the + // next regex match. + while (match = versionRegExp.exec(userAgent)) { + data.push([ + match[1], // key + match[2], // value + // || undefined as this is not undefined in IE7 and IE8 + match[3] || undefined // info + ]); + } + + return data; +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for manipulating objects/maps/hashes. + * @author arv@google.com (Erik Arvidsson) + */ + +goog.provide('goog.object'); + + +/** + * Whether two values are not observably distinguishable. This + * correctly detects that 0 is not the same as -0 and two NaNs are + * practically equivalent. + * + * The implementation is as suggested by harmony:egal proposal. + * + * @param {*} v The first value to compare. + * @param {*} v2 The second value to compare. + * @return {boolean} Whether two values are not observably distinguishable. + * @see http://wiki.ecmascript.org/doku.php?id=harmony:egal + */ +goog.object.is = function(v, v2) { + if (v === v2) { + // 0 === -0, but they are not identical. + // We need the cast because the compiler requires that v2 is a + // number (although 1/v2 works with non-number). We cast to ? to + // stop the compiler from type-checking this statement. + return v !== 0 || 1 / v === 1 / /** @type {?} */ (v2); + } + + // NaN is non-reflexive: NaN !== NaN, although they are identical. + return v !== v && v2 !== v2; +}; + + +/** + * Calls a function for each element in an object/map/hash. + * + * @param {Object<K,V>} obj The object over which to iterate. + * @param {function(this:T,V,?,Object<K,V>):?} f The function to call + * for every element. This function takes 3 arguments (the value, the + * key and the object) and the return value is ignored. + * @param {T=} opt_obj This is used as the 'this' object within f. + * @template T,K,V + */ +goog.object.forEach = function(obj, f, opt_obj) { + for (var key in obj) { + f.call(/** @type {?} */ (opt_obj), obj[key], key, obj); + } +}; + + +/** + * Calls a function for each element in an object/map/hash. If that call returns + * true, adds the element to a new object. + * + * @param {Object<K,V>} obj The object over which to iterate. + * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to call + * for every element. This + * function takes 3 arguments (the value, the key and the object) + * and should return a boolean. If the return value is true the + * element is added to the result object. If it is false the + * element is not included. + * @param {T=} opt_obj This is used as the 'this' object within f. + * @return {!Object<K,V>} a new object in which only elements that passed the + * test are present. + * @template T,K,V + */ +goog.object.filter = function(obj, f, opt_obj) { + var res = {}; + for (var key in obj) { + if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) { + res[key] = obj[key]; + } + } + return res; +}; + + +/** + * For every element in an object/map/hash calls a function and inserts the + * result into a new object. + * + * @param {Object<K,V>} obj The object over which to iterate. + * @param {function(this:T,V,?,Object<K,V>):R} f The function to call + * for every element. This function + * takes 3 arguments (the value, the key and the object) + * and should return something. The result will be inserted + * into a new object. + * @param {T=} opt_obj This is used as the 'this' object within f. + * @return {!Object<K,R>} a new object with the results from f. + * @template T,K,V,R + */ +goog.object.map = function(obj, f, opt_obj) { + var res = {}; + for (var key in obj) { + res[key] = f.call(/** @type {?} */ (opt_obj), obj[key], key, obj); + } + return res; +}; + + +/** + * Calls a function for each element in an object/map/hash. If any + * call returns true, returns true (without checking the rest). If + * all calls return false, returns false. + * + * @param {Object<K,V>} obj The object to check. + * @param {function(this:T,V,?,Object<K,V>):boolean} f The function to + * call for every element. This function + * takes 3 arguments (the value, the key and the object) and should + * return a boolean. + * @param {T=} opt_obj This is used as the 'this' object within f. + * @return {boolean} true if any element passes the test. + * @template T,K,V + */ +goog.object.some = function(obj, f, opt_obj) { + for (var key in obj) { + if (f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) { + return true; + } + } + return false; +}; + + +/** + * Calls a function for each element in an object/map/hash. If + * all calls return true, returns true. If any call returns false, returns + * false at this point and does not continue to check the remaining elements. + * + * @param {Object<K,V>} obj The object to check. + * @param {?function(this:T,V,?,Object<K,V>):boolean} f The function to + * call for every element. This function + * takes 3 arguments (the value, the key and the object) and should + * return a boolean. + * @param {T=} opt_obj This is used as the 'this' object within f. + * @return {boolean} false if any element fails the test. + * @template T,K,V + */ +goog.object.every = function(obj, f, opt_obj) { + for (var key in obj) { + if (!f.call(/** @type {?} */ (opt_obj), obj[key], key, obj)) { + return false; + } + } + return true; +}; + + +/** + * Returns the number of key-value pairs in the object map. + * + * @param {Object} obj The object for which to get the number of key-value + * pairs. + * @return {number} The number of key-value pairs in the object map. + */ +goog.object.getCount = function(obj) { + var rv = 0; + for (var key in obj) { + rv++; + } + return rv; +}; + + +/** + * Returns one key from the object map, if any exists. + * For map literals the returned key will be the first one in most of the + * browsers (a know exception is Konqueror). + * + * @param {Object} obj The object to pick a key from. + * @return {string|undefined} The key or undefined if the object is empty. + */ +goog.object.getAnyKey = function(obj) { + for (var key in obj) { + return key; + } +}; + + +/** + * Returns one value from the object map, if any exists. + * For map literals the returned value will be the first one in most of the + * browsers (a know exception is Konqueror). + * + * @param {Object<K,V>} obj The object to pick a value from. + * @return {V|undefined} The value or undefined if the object is empty. + * @template K,V + */ +goog.object.getAnyValue = function(obj) { + for (var key in obj) { + return obj[key]; + } +}; + + +/** + * Whether the object/hash/map contains the given object as a value. + * An alias for goog.object.containsValue(obj, val). + * + * @param {Object<K,V>} obj The object in which to look for val. + * @param {V} val The object for which to check. + * @return {boolean} true if val is present. + * @template K,V + */ +goog.object.contains = function(obj, val) { + return goog.object.containsValue(obj, val); +}; + + +/** + * Returns the values of the object/map/hash. + * + * @param {Object<K,V>} obj The object from which to get the values. + * @return {!Array<V>} The values in the object/map/hash. + * @template K,V + */ +goog.object.getValues = function(obj) { + var res = []; + var i = 0; + for (var key in obj) { + res[i++] = obj[key]; + } + return res; +}; + + +/** + * Returns the keys of the object/map/hash. + * + * @param {Object} obj The object from which to get the keys. + * @return {!Array<string>} Array of property keys. + */ +goog.object.getKeys = function(obj) { + var res = []; + var i = 0; + for (var key in obj) { + res[i++] = key; + } + return res; +}; + + +/** + * Get a value from an object multiple levels deep. This is useful for + * pulling values from deeply nested objects, such as JSON responses. + * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3) + * + * @param {!Object} obj An object to get the value from. Can be array-like. + * @param {...(string|number|!IArrayLike<number|string>)} + * var_args A number of keys + * (as strings, or numbers, for array-like objects). Can also be + * specified as a single array of keys. + * @return {*} The resulting value. If, at any point, the value for a key + * is undefined, returns undefined. + */ +goog.object.getValueByKeys = function(obj, var_args) { + var isArrayLike = goog.isArrayLike(var_args); + var keys = isArrayLike ? var_args : arguments; + + // Start with the 2nd parameter for the variable parameters syntax. + for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) { + obj = obj[keys[i]]; + if (!goog.isDef(obj)) { + break; + } + } + + return obj; +}; + + +/** + * Whether the object/map/hash contains the given key. + * + * @param {Object} obj The object in which to look for key. + * @param {?} key The key for which to check. + * @return {boolean} true If the map contains the key. + */ +goog.object.containsKey = function(obj, key) { + return obj !== null && key in obj; +}; + + +/** + * Whether the object/map/hash contains the given value. This is O(n). + * + * @param {Object<K,V>} obj The object in which to look for val. + * @param {V} val The value for which to check. + * @return {boolean} true If the map contains the value. + * @template K,V + */ +goog.object.containsValue = function(obj, val) { + for (var key in obj) { + if (obj[key] == val) { + return true; + } + } + return false; +}; + + +/** + * Searches an object for an element that satisfies the given condition and + * returns its key. + * @param {Object<K,V>} obj The object to search in. + * @param {function(this:T,V,string,Object<K,V>):boolean} f The + * function to call for every element. Takes 3 arguments (the value, + * the key and the object) and should return a boolean. + * @param {T=} opt_this An optional "this" context for the function. + * @return {string|undefined} The key of an element for which the function + * returns true or undefined if no such element is found. + * @template T,K,V + */ +goog.object.findKey = function(obj, f, opt_this) { + for (var key in obj) { + if (f.call(/** @type {?} */ (opt_this), obj[key], key, obj)) { + return key; + } + } + return undefined; +}; + + +/** + * Searches an object for an element that satisfies the given condition and + * returns its value. + * @param {Object<K,V>} obj The object to search in. + * @param {function(this:T,V,string,Object<K,V>):boolean} f The function + * to call for every element. Takes 3 arguments (the value, the key + * and the object) and should return a boolean. + * @param {T=} opt_this An optional "this" context for the function. + * @return {V} The value of an element for which the function returns true or + * undefined if no such element is found. + * @template T,K,V + */ +goog.object.findValue = function(obj, f, opt_this) { + var key = goog.object.findKey(obj, f, opt_this); + return key && obj[key]; +}; + + +/** + * Whether the object/map/hash is empty. + * + * @param {Object} obj The object to test. + * @return {boolean} true if obj is empty. + */ +goog.object.isEmpty = function(obj) { + for (var key in obj) { + return false; + } + return true; +}; + + +/** + * Removes all key value pairs from the object/map/hash. + * + * @param {Object} obj The object to clear. + */ +goog.object.clear = function(obj) { + for (var i in obj) { + delete obj[i]; + } +}; + + +/** + * Removes a key-value pair based on the key. + * + * @param {Object} obj The object from which to remove the key. + * @param {?} key The key to remove. + * @return {boolean} Whether an element was removed. + */ +goog.object.remove = function(obj, key) { + var rv; + if (rv = key in /** @type {!Object} */ (obj)) { + delete obj[key]; + } + return rv; +}; + + +/** + * Adds a key-value pair to the object. Throws an exception if the key is + * already in use. Use set if you want to change an existing pair. + * + * @param {Object<K,V>} obj The object to which to add the key-value pair. + * @param {string} key The key to add. + * @param {V} val The value to add. + * @template K,V + */ +goog.object.add = function(obj, key, val) { + if (obj !== null && key in obj) { + throw Error('The object already contains the key "' + key + '"'); + } + goog.object.set(obj, key, val); +}; + + +/** + * Returns the value for the given key. + * + * @param {Object<K,V>} obj The object from which to get the value. + * @param {string} key The key for which to get the value. + * @param {R=} opt_val The value to return if no item is found for the given + * key (default is undefined). + * @return {V|R|undefined} The value for the given key. + * @template K,V,R + */ +goog.object.get = function(obj, key, opt_val) { + if (obj !== null && key in obj) { + return obj[key]; + } + return opt_val; +}; + + +/** + * Adds a key-value pair to the object/map/hash. + * + * @param {Object<K,V>} obj The object to which to add the key-value pair. + * @param {string} key The key to add. + * @param {V} value The value to add. + * @template K,V + */ +goog.object.set = function(obj, key, value) { + obj[key] = value; +}; + + +/** + * Adds a key-value pair to the object/map/hash if it doesn't exist yet. + * + * @param {Object<K,V>} obj The object to which to add the key-value pair. + * @param {string} key The key to add. + * @param {V} value The value to add if the key wasn't present. + * @return {V} The value of the entry at the end of the function. + * @template K,V + */ +goog.object.setIfUndefined = function(obj, key, value) { + return key in /** @type {!Object} */ (obj) ? obj[key] : (obj[key] = value); +}; + + +/** + * Sets a key and value to an object if the key is not set. The value will be + * the return value of the given function. If the key already exists, the + * object will not be changed and the function will not be called (the function + * will be lazily evaluated -- only called if necessary). + * + * This function is particularly useful for use with a map used a as a cache. + * + * @param {!Object<K,V>} obj The object to which to add the key-value pair. + * @param {string} key The key to add. + * @param {function():V} f The value to add if the key wasn't present. + * @return {V} The value of the entry at the end of the function. + * @template K,V + */ +goog.object.setWithReturnValueIfNotSet = function(obj, key, f) { + if (key in obj) { + return obj[key]; + } + + var val = f(); + obj[key] = val; + return val; +}; + + +/** + * Compares two objects for equality using === on the values. + * + * @param {!Object<K,V>} a + * @param {!Object<K,V>} b + * @return {boolean} + * @template K,V + */ +goog.object.equals = function(a, b) { + for (var k in a) { + if (!(k in b) || a[k] !== b[k]) { + return false; + } + } + for (var k in b) { + if (!(k in a)) { + return false; + } + } + return true; +}; + + +/** + * Returns a shallow clone of the object. + * + * @param {Object<K,V>} obj Object to clone. + * @return {!Object<K,V>} Clone of the input object. + * @template K,V + */ +goog.object.clone = function(obj) { + // We cannot use the prototype trick because a lot of methods depend on where + // the actual key is set. + + var res = {}; + for (var key in obj) { + res[key] = obj[key]; + } + return res; + // We could also use goog.mixin but I wanted this to be independent from that. +}; + + +/** + * Clones a value. The input may be an Object, Array, or basic type. Objects and + * arrays will be cloned recursively. + * + * WARNINGS: + * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects + * that refer to themselves will cause infinite recursion. + * + * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and + * copies UIDs created by <code>getUid</code> into cloned results. + * + * @param {*} obj The value to clone. + * @return {*} A clone of the input value. + */ +goog.object.unsafeClone = function(obj) { + var type = goog.typeOf(obj); + if (type == 'object' || type == 'array') { + if (goog.isFunction(obj.clone)) { + return obj.clone(); + } + var clone = type == 'array' ? [] : {}; + for (var key in obj) { + clone[key] = goog.object.unsafeClone(obj[key]); + } + return clone; + } + + return obj; +}; + + +/** + * Returns a new object in which all the keys and values are interchanged + * (keys become values and values become keys). If multiple keys map to the + * same value, the chosen transposed value is implementation-dependent. + * + * @param {Object} obj The object to transpose. + * @return {!Object} The transposed object. + */ +goog.object.transpose = function(obj) { + var transposed = {}; + for (var key in obj) { + transposed[obj[key]] = key; + } + return transposed; +}; + + +/** + * The names of the fields that are defined on Object.prototype. + * @type {Array<string>} + * @private + */ +goog.object.PROTOTYPE_FIELDS_ = [ + 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', + 'toLocaleString', 'toString', 'valueOf' +]; + + +/** + * Extends an object with another object. + * This operates 'in-place'; it does not create a new Object. + * + * Example: + * var o = {}; + * goog.object.extend(o, {a: 0, b: 1}); + * o; // {a: 0, b: 1} + * goog.object.extend(o, {b: 2, c: 3}); + * o; // {a: 0, b: 2, c: 3} + * + * @param {Object} target The object to modify. Existing properties will be + * overwritten if they are also present in one of the objects in + * {@code var_args}. + * @param {...Object} var_args The objects from which values will be copied. + */ +goog.object.extend = function(target, var_args) { + var key, source; + for (var i = 1; i < arguments.length; i++) { + source = arguments[i]; + for (key in source) { + target[key] = source[key]; + } + + // For IE the for-in-loop does not contain any properties that are not + // enumerable on the prototype object (for example isPrototypeOf from + // Object.prototype) and it will also not include 'replace' on objects that + // extend String and change 'replace' (not that it is common for anyone to + // extend anything except Object). + + for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) { + key = goog.object.PROTOTYPE_FIELDS_[j]; + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } +}; + + +/** + * Creates a new object built from the key-value pairs provided as arguments. + * @param {...*} var_args If only one argument is provided and it is an array + * then this is used as the arguments, otherwise even arguments are used as + * the property names and odd arguments are used as the property values. + * @return {!Object} The new object. + * @throws {Error} If there are uneven number of arguments or there is only one + * non array argument. + */ +goog.object.create = function(var_args) { + var argLength = arguments.length; + if (argLength == 1 && goog.isArray(arguments[0])) { + return goog.object.create.apply(null, arguments[0]); + } + + if (argLength % 2) { + throw Error('Uneven number of arguments'); + } + + var rv = {}; + for (var i = 0; i < argLength; i += 2) { + rv[arguments[i]] = arguments[i + 1]; + } + return rv; +}; + + +/** + * Creates a new object where the property names come from the arguments but + * the value is always set to true + * @param {...*} var_args If only one argument is provided and it is an array + * then this is used as the arguments, otherwise the arguments are used + * as the property names. + * @return {!Object} The new object. + */ +goog.object.createSet = function(var_args) { + var argLength = arguments.length; + if (argLength == 1 && goog.isArray(arguments[0])) { + return goog.object.createSet.apply(null, arguments[0]); + } + + var rv = {}; + for (var i = 0; i < argLength; i++) { + rv[arguments[i]] = true; + } + return rv; +}; + + +/** + * Creates an immutable view of the underlying object, if the browser + * supports immutable objects. + * + * In default mode, writes to this view will fail silently. In strict mode, + * they will throw an error. + * + * @param {!Object<K,V>} obj An object. + * @return {!Object<K,V>} An immutable view of that object, or the + * original object if this browser does not support immutables. + * @template K,V + */ +goog.object.createImmutableView = function(obj) { + var result = obj; + if (Object.isFrozen && !Object.isFrozen(obj)) { + result = Object.create(obj); + Object.freeze(result); + } + return result; +}; + + +/** + * @param {!Object} obj An object. + * @return {boolean} Whether this is an immutable view of the object. + */ +goog.object.isImmutableView = function(obj) { + return !!Object.isFrozen && Object.isFrozen(obj); +}; + +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Closure user agent detection (Browser). + * @see <a href="http://www.useragentstring.com/">User agent strings</a> + * For more information on rendering engine, platform, or device see the other + * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform, + * goog.labs.userAgent.device respectively.) + * + * @author martone@google.com (Andy Martone) + */ + +goog.provide('goog.labs.userAgent.browser'); + +goog.require('goog.array'); +goog.require('goog.labs.userAgent.util'); +goog.require('goog.object'); +goog.require('goog.string'); + + +// TODO(nnaze): Refactor to remove excessive exclusion logic in matching +// functions. + + +/** + * @return {boolean} Whether the user's browser is Opera. Note: Chromium + * based Opera (Opera 15+) is detected as Chrome to avoid unnecessary + * special casing. + * @private + */ +goog.labs.userAgent.browser.matchOpera_ = function() { + return goog.labs.userAgent.util.matchUserAgent('Opera'); +}; + + +/** + * @return {boolean} Whether the user's browser is IE. + * @private + */ +goog.labs.userAgent.browser.matchIE_ = function() { + return goog.labs.userAgent.util.matchUserAgent('Trident') || + goog.labs.userAgent.util.matchUserAgent('MSIE'); +}; + + +/** + * @return {boolean} Whether the user's browser is Edge. + * @private + */ +goog.labs.userAgent.browser.matchEdge_ = function() { + return goog.labs.userAgent.util.matchUserAgent('Edge'); +}; + + +/** + * @return {boolean} Whether the user's browser is Firefox. + * @private + */ +goog.labs.userAgent.browser.matchFirefox_ = function() { + return goog.labs.userAgent.util.matchUserAgent('Firefox'); +}; + + +/** + * @return {boolean} Whether the user's browser is Safari. + * @private + */ +goog.labs.userAgent.browser.matchSafari_ = function() { + return goog.labs.userAgent.util.matchUserAgent('Safari') && + !(goog.labs.userAgent.browser.matchChrome_() || + goog.labs.userAgent.browser.matchCoast_() || + goog.labs.userAgent.browser.matchOpera_() || + goog.labs.userAgent.browser.matchEdge_() || + goog.labs.userAgent.browser.isSilk() || + goog.labs.userAgent.util.matchUserAgent('Android')); +}; + + +/** + * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based + * iOS browser). + * @private + */ +goog.labs.userAgent.browser.matchCoast_ = function() { + return goog.labs.userAgent.util.matchUserAgent('Coast'); +}; + + +/** + * @return {boolean} Whether the user's browser is iOS Webview. + * @private + */ +goog.labs.userAgent.browser.matchIosWebview_ = function() { + // iOS Webview does not show up as Chrome or Safari. Also check for Opera's + // WebKit-based iOS browser, Coast. + return (goog.labs.userAgent.util.matchUserAgent('iPad') || + goog.labs.userAgent.util.matchUserAgent('iPhone')) && + !goog.labs.userAgent.browser.matchSafari_() && + !goog.labs.userAgent.browser.matchChrome_() && + !goog.labs.userAgent.browser.matchCoast_() && + goog.labs.userAgent.util.matchUserAgent('AppleWebKit'); +}; + + +/** + * @return {boolean} Whether the user's browser is Chrome. + * @private + */ +goog.labs.userAgent.browser.matchChrome_ = function() { + return (goog.labs.userAgent.util.matchUserAgent('Chrome') || + goog.labs.userAgent.util.matchUserAgent('CriOS')) && + !goog.labs.userAgent.browser.matchEdge_(); +}; + + +/** + * @return {boolean} Whether the user's browser is the Android browser. + * @private + */ +goog.labs.userAgent.browser.matchAndroidBrowser_ = function() { + // Android can appear in the user agent string for Chrome on Android. + // This is not the Android standalone browser if it does. + return goog.labs.userAgent.util.matchUserAgent('Android') && + !(goog.labs.userAgent.browser.isChrome() || + goog.labs.userAgent.browser.isFirefox() || + goog.labs.userAgent.browser.isOpera() || + goog.labs.userAgent.browser.isSilk()); +}; + + +/** + * @return {boolean} Whether the user's browser is Opera. + */ +goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_; + + +/** + * @return {boolean} Whether the user's browser is IE. + */ +goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_; + + +/** + * @return {boolean} Whether the user's browser is Edge. + */ +goog.labs.userAgent.browser.isEdge = goog.labs.userAgent.browser.matchEdge_; + + +/** + * @return {boolean} Whether the user's browser is Firefox. + */ +goog.labs.userAgent.browser.isFirefox = + goog.labs.userAgent.browser.matchFirefox_; + + +/** + * @return {boolean} Whether the user's browser is Safari. + */ +goog.labs.userAgent.browser.isSafari = goog.labs.userAgent.browser.matchSafari_; + + +/** + * @return {boolean} Whether the user's browser is Coast (Opera's Webkit-based + * iOS browser). + */ +goog.labs.userAgent.browser.isCoast = goog.labs.userAgent.browser.matchCoast_; + + +/** + * @return {boolean} Whether the user's browser is iOS Webview. + */ +goog.labs.userAgent.browser.isIosWebview = + goog.labs.userAgent.browser.matchIosWebview_; + + +/** + * @return {boolean} Whether the user's browser is Chrome. + */ +goog.labs.userAgent.browser.isChrome = goog.labs.userAgent.browser.matchChrome_; + + +/** + * @return {boolean} Whether the user's browser is the Android browser. + */ +goog.labs.userAgent.browser.isAndroidBrowser = + goog.labs.userAgent.browser.matchAndroidBrowser_; + + +/** + * For more information, see: + * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html + * @return {boolean} Whether the user's browser is Silk. + */ +goog.labs.userAgent.browser.isSilk = function() { + return goog.labs.userAgent.util.matchUserAgent('Silk'); +}; + + +/** + * @return {string} The browser version or empty string if version cannot be + * determined. Note that for Internet Explorer, this returns the version of + * the browser, not the version of the rendering engine. (IE 8 in + * compatibility mode will return 8.0 rather than 7.0. To determine the + * rendering engine version, look at document.documentMode instead. See + * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more + * details.) + */ +goog.labs.userAgent.browser.getVersion = function() { + var userAgentString = goog.labs.userAgent.util.getUserAgent(); + // Special case IE since IE's version is inside the parenthesis and + // without the '/'. + if (goog.labs.userAgent.browser.isIE()) { + return goog.labs.userAgent.browser.getIEVersion_(userAgentString); + } + + var versionTuples = + goog.labs.userAgent.util.extractVersionTuples(userAgentString); + + // Construct a map for easy lookup. + var versionMap = {}; + goog.array.forEach(versionTuples, function(tuple) { + // Note that the tuple is of length three, but we only care about the + // first two. + var key = tuple[0]; + var value = tuple[1]; + versionMap[key] = value; + }); + + var versionMapHasKey = goog.partial(goog.object.containsKey, versionMap); + + // Gives the value with the first key it finds, otherwise empty string. + function lookUpValueWithKeys(keys) { + var key = goog.array.find(keys, versionMapHasKey); + return versionMap[key] || ''; + } + + // Check Opera before Chrome since Opera 15+ has "Chrome" in the string. + // See + // http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond + if (goog.labs.userAgent.browser.isOpera()) { + // Opera 10 has Version/10.0 but Opera/9.8, so look for "Version" first. + // Opera uses 'OPR' for more recent UAs. + return lookUpValueWithKeys(['Version', 'Opera']); + } + + // Check Edge before Chrome since it has Chrome in the string. + if (goog.labs.userAgent.browser.isEdge()) { + return lookUpValueWithKeys(['Edge']); + } + + if (goog.labs.userAgent.browser.isChrome()) { + return lookUpValueWithKeys(['Chrome', 'CriOS']); + } + + // Usually products browser versions are in the third tuple after "Mozilla" + // and the engine. + var tuple = versionTuples[2]; + return tuple && tuple[1] || ''; +}; + + +/** + * @param {string|number} version The version to check. + * @return {boolean} Whether the browser version is higher or the same as the + * given version. + */ +goog.labs.userAgent.browser.isVersionOrHigher = function(version) { + return goog.string.compareVersions( + goog.labs.userAgent.browser.getVersion(), version) >= 0; +}; + + +/** + * Determines IE version. More information: + * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString + * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx + * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx + * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx + * + * @param {string} userAgent the User-Agent. + * @return {string} + * @private + */ +goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) { + // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade + // bug. Example UA: + // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) + // like Gecko. + // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments. + var rv = /rv: *([\d\.]*)/.exec(userAgent); + if (rv && rv[1]) { + return rv[1]; + } + + var version = ''; + var msie = /MSIE +([\d\.]+)/.exec(userAgent); + if (msie && msie[1]) { + // IE in compatibility mode usually identifies itself as MSIE 7.0; in this + // case, use the Trident version to determine the version of IE. For more + // details, see the links above. + var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent); + if (msie[1] == '7.0') { + if (tridentVersion && tridentVersion[1]) { + switch (tridentVersion[1]) { + case '4.0': + version = '8.0'; + break; + case '5.0': + version = '9.0'; + break; + case '6.0': + version = '10.0'; + break; + case '7.0': + version = '11.0'; + break; + } + } else { + version = '7.0'; + } + } else { + version = msie[1]; + } + } + return version; +}; + +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Closure user agent detection. + * @see http://en.wikipedia.org/wiki/User_agent + * For more information on browser brand, platform, or device see the other + * sub-namespaces in goog.labs.userAgent (browser, platform, and device). + * + */ + +goog.provide('goog.labs.userAgent.engine'); + +goog.require('goog.array'); +goog.require('goog.labs.userAgent.util'); +goog.require('goog.string'); + + +/** + * @return {boolean} Whether the rendering engine is Presto. + */ +goog.labs.userAgent.engine.isPresto = function() { + return goog.labs.userAgent.util.matchUserAgent('Presto'); +}; + + +/** + * @return {boolean} Whether the rendering engine is Trident. + */ +goog.labs.userAgent.engine.isTrident = function() { + // IE only started including the Trident token in IE8. + return goog.labs.userAgent.util.matchUserAgent('Trident') || + goog.labs.userAgent.util.matchUserAgent('MSIE'); +}; + + +/** + * @return {boolean} Whether the rendering engine is Edge. + */ +goog.labs.userAgent.engine.isEdge = function() { + return goog.labs.userAgent.util.matchUserAgent('Edge'); +}; + + +/** + * @return {boolean} Whether the rendering engine is WebKit. + */ +goog.labs.userAgent.engine.isWebKit = function() { + return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit') && + !goog.labs.userAgent.engine.isEdge(); +}; + + +/** + * @return {boolean} Whether the rendering engine is Gecko. + */ +goog.labs.userAgent.engine.isGecko = function() { + return goog.labs.userAgent.util.matchUserAgent('Gecko') && + !goog.labs.userAgent.engine.isWebKit() && + !goog.labs.userAgent.engine.isTrident() && + !goog.labs.userAgent.engine.isEdge(); +}; + + +/** + * @return {string} The rendering engine's version or empty string if version + * can't be determined. + */ +goog.labs.userAgent.engine.getVersion = function() { + var userAgentString = goog.labs.userAgent.util.getUserAgent(); + if (userAgentString) { + var tuples = goog.labs.userAgent.util.extractVersionTuples(userAgentString); + + var engineTuple = goog.labs.userAgent.engine.getEngineTuple_(tuples); + if (engineTuple) { + // In Gecko, the version string is either in the browser info or the + // Firefox version. See Gecko user agent string reference: + // http://goo.gl/mULqa + if (engineTuple[0] == 'Gecko') { + return goog.labs.userAgent.engine.getVersionForKey_(tuples, 'Firefox'); + } + + return engineTuple[1]; + } + + // MSIE has only one version identifier, and the Trident version is + // specified in the parenthetical. IE Edge is covered in the engine tuple + // detection. + var browserTuple = tuples[0]; + var info; + if (browserTuple && (info = browserTuple[2])) { + var match = /Trident\/([^\s;]+)/.exec(info); + if (match) { + return match[1]; + } + } + } + return ''; +}; + + +/** + * @param {!Array<!Array<string>>} tuples Extracted version tuples. + * @return {!Array<string>|undefined} The engine tuple or undefined if not + * found. + * @private + */ +goog.labs.userAgent.engine.getEngineTuple_ = function(tuples) { + if (!goog.labs.userAgent.engine.isEdge()) { + return tuples[1]; + } + for (var i = 0; i < tuples.length; i++) { + var tuple = tuples[i]; + if (tuple[0] == 'Edge') { + return tuple; + } + } +}; + + +/** + * @param {string|number} version The version to check. + * @return {boolean} Whether the rendering engine version is higher or the same + * as the given version. + */ +goog.labs.userAgent.engine.isVersionOrHigher = function(version) { + return goog.string.compareVersions( + goog.labs.userAgent.engine.getVersion(), version) >= 0; +}; + + +/** + * @param {!Array<!Array<string>>} tuples Version tuples. + * @param {string} key The key to look for. + * @return {string} The version string of the given key, if present. + * Otherwise, the empty string. + * @private + */ +goog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) { + // TODO(nnaze): Move to util if useful elsewhere. + + var pair = goog.array.find(tuples, function(pair) { return key == pair[0]; }); + + return pair && pair[1] || ''; +}; + +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Closure user agent platform detection. + * @see <a href="http://www.useragentstring.com/">User agent strings</a> + * For more information on browser brand, rendering engine, or device see the + * other sub-namespaces in goog.labs.userAgent (browser, engine, and device + * respectively). + * + */ + +goog.provide('goog.labs.userAgent.platform'); + +goog.require('goog.labs.userAgent.util'); +goog.require('goog.string'); + + +/** + * @return {boolean} Whether the platform is Android. + */ +goog.labs.userAgent.platform.isAndroid = function() { + return goog.labs.userAgent.util.matchUserAgent('Android'); +}; + + +/** + * @return {boolean} Whether the platform is iPod. + */ +goog.labs.userAgent.platform.isIpod = function() { + return goog.labs.userAgent.util.matchUserAgent('iPod'); +}; + + +/** + * @return {boolean} Whether the platform is iPhone. + */ +goog.labs.userAgent.platform.isIphone = function() { + return goog.labs.userAgent.util.matchUserAgent('iPhone') && + !goog.labs.userAgent.util.matchUserAgent('iPod') && + !goog.labs.userAgent.util.matchUserAgent('iPad'); +}; + + +/** + * @return {boolean} Whether the platform is iPad. + */ +goog.labs.userAgent.platform.isIpad = function() { + return goog.labs.userAgent.util.matchUserAgent('iPad'); +}; + + +/** + * @return {boolean} Whether the platform is iOS. + */ +goog.labs.userAgent.platform.isIos = function() { + return goog.labs.userAgent.platform.isIphone() || + goog.labs.userAgent.platform.isIpad() || + goog.labs.userAgent.platform.isIpod(); +}; + + +/** + * @return {boolean} Whether the platform is Mac. + */ +goog.labs.userAgent.platform.isMacintosh = function() { + return goog.labs.userAgent.util.matchUserAgent('Macintosh'); +}; + + +/** + * Note: ChromeOS is not considered to be Linux as it does not report itself + * as Linux in the user agent string. + * @return {boolean} Whether the platform is Linux. + */ +goog.labs.userAgent.platform.isLinux = function() { + return goog.labs.userAgent.util.matchUserAgent('Linux'); +}; + + +/** + * @return {boolean} Whether the platform is Windows. + */ +goog.labs.userAgent.platform.isWindows = function() { + return goog.labs.userAgent.util.matchUserAgent('Windows'); +}; + + +/** + * @return {boolean} Whether the platform is ChromeOS. + */ +goog.labs.userAgent.platform.isChromeOS = function() { + return goog.labs.userAgent.util.matchUserAgent('CrOS'); +}; + + +/** + * The version of the platform. We only determine the version for Windows, + * Mac, and Chrome OS. It doesn't make much sense on Linux. For Windows, we only + * look at the NT version. Non-NT-based versions (e.g. 95, 98, etc.) are given + * version 0.0. + * + * @return {string} The platform version or empty string if version cannot be + * determined. + */ +goog.labs.userAgent.platform.getVersion = function() { + var userAgentString = goog.labs.userAgent.util.getUserAgent(); + var version = '', re; + if (goog.labs.userAgent.platform.isWindows()) { + re = /Windows (?:NT|Phone) ([0-9.]+)/; + var match = re.exec(userAgentString); + if (match) { + version = match[1]; + } else { + version = '0.0'; + } + } else if (goog.labs.userAgent.platform.isIos()) { + re = /(?:iPhone|iPod|iPad|CPU)\s+OS\s+(\S+)/; + var match = re.exec(userAgentString); + // Report the version as x.y.z and not x_y_z + version = match && match[1].replace(/_/g, '.'); + } else if (goog.labs.userAgent.platform.isMacintosh()) { + re = /Mac OS X ([0-9_.]+)/; + var match = re.exec(userAgentString); + // Note: some old versions of Camino do not report an OSX version. + // Default to 10. + version = match ? match[1].replace(/_/g, '.') : '10'; + } else if (goog.labs.userAgent.platform.isAndroid()) { + re = /Android\s+([^\);]+)(\)|;)/; + var match = re.exec(userAgentString); + version = match && match[1]; + } else if (goog.labs.userAgent.platform.isChromeOS()) { + re = /(?:CrOS\s+(?:i686|x86_64)\s+([0-9.]+))/; + var match = re.exec(userAgentString); + version = match && match[1]; + } + return version || ''; +}; + + +/** + * @param {string|number} version The version to check. + * @return {boolean} Whether the browser version is higher or the same as the + * given version. + */ +goog.labs.userAgent.platform.isVersionOrHigher = function(version) { + return goog.string.compareVersions( + goog.labs.userAgent.platform.getVersion(), version) >= 0; +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Rendering engine detection. + * @see <a href="http://www.useragentstring.com/">User agent strings</a> + * For information on the browser brand (such as Safari versus Chrome), see + * goog.userAgent.product. + * @author arv@google.com (Erik Arvidsson) + * @see ../demos/useragent.html + */ + +goog.provide('goog.userAgent'); + +goog.require('goog.labs.userAgent.browser'); +goog.require('goog.labs.userAgent.engine'); +goog.require('goog.labs.userAgent.platform'); +goog.require('goog.labs.userAgent.util'); +goog.require('goog.string'); + + +/** + * @define {boolean} Whether we know at compile-time that the browser is IE. + */ +goog.define('goog.userAgent.ASSUME_IE', false); + + +/** + * @define {boolean} Whether we know at compile-time that the browser is EDGE. + */ +goog.define('goog.userAgent.ASSUME_EDGE', false); + + +/** + * @define {boolean} Whether we know at compile-time that the browser is GECKO. + */ +goog.define('goog.userAgent.ASSUME_GECKO', false); + + +/** + * @define {boolean} Whether we know at compile-time that the browser is WEBKIT. + */ +goog.define('goog.userAgent.ASSUME_WEBKIT', false); + + +/** + * @define {boolean} Whether we know at compile-time that the browser is a + * mobile device running WebKit e.g. iPhone or Android. + */ +goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false); + + +/** + * @define {boolean} Whether we know at compile-time that the browser is OPERA. + */ +goog.define('goog.userAgent.ASSUME_OPERA', false); + + +/** + * @define {boolean} Whether the + * {@code goog.userAgent.isVersionOrHigher} + * function will return true for any version. + */ +goog.define('goog.userAgent.ASSUME_ANY_VERSION', false); + + +/** + * Whether we know the browser engine at compile-time. + * @type {boolean} + * @private + */ +goog.userAgent.BROWSER_KNOWN_ = goog.userAgent.ASSUME_IE || + goog.userAgent.ASSUME_EDGE || goog.userAgent.ASSUME_GECKO || + goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.ASSUME_WEBKIT || + goog.userAgent.ASSUME_OPERA; + + +/** + * Returns the userAgent string for the current browser. + * + * @return {string} The userAgent string. + */ +goog.userAgent.getUserAgentString = function() { + return goog.labs.userAgent.util.getUserAgent(); +}; + + +/** + * TODO(nnaze): Change type to "Navigator" and update compilation targets. + * @return {Object} The native navigator object. + */ +goog.userAgent.getNavigator = function() { + // Need a local navigator reference instead of using the global one, + // to avoid the rare case where they reference different objects. + // (in a WorkerPool, for example). + return goog.global['navigator'] || null; +}; + + +/** + * Whether the user agent is Opera. + * @type {boolean} + */ +goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ? + goog.userAgent.ASSUME_OPERA : + goog.labs.userAgent.browser.isOpera(); + + +/** + * Whether the user agent is Internet Explorer. + * @type {boolean} + */ +goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ? + goog.userAgent.ASSUME_IE : + goog.labs.userAgent.browser.isIE(); + + +/** + * Whether the user agent is Microsoft Edge. + * @type {boolean} + */ +goog.userAgent.EDGE = goog.userAgent.BROWSER_KNOWN_ ? + goog.userAgent.ASSUME_EDGE : + goog.labs.userAgent.engine.isEdge(); + + +/** + * Whether the user agent is MS Internet Explorer or MS Edge. + * @type {boolean} + */ +goog.userAgent.EDGE_OR_IE = goog.userAgent.EDGE || goog.userAgent.IE; + + +/** + * Whether the user agent is Gecko. Gecko is the rendering engine used by + * Mozilla, Firefox, and others. + * @type {boolean} + */ +goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ? + goog.userAgent.ASSUME_GECKO : + goog.labs.userAgent.engine.isGecko(); + + +/** + * Whether the user agent is WebKit. WebKit is the rendering engine that + * Safari, Android and others use. + * @type {boolean} + */ +goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ? + goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT : + goog.labs.userAgent.engine.isWebKit(); + + +/** + * Whether the user agent is running on a mobile device. + * + * This is a separate function so that the logic can be tested. + * + * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile(). + * + * @return {boolean} Whether the user agent is running on a mobile device. + * @private + */ +goog.userAgent.isMobile_ = function() { + return goog.userAgent.WEBKIT && + goog.labs.userAgent.util.matchUserAgent('Mobile'); +}; + + +/** + * Whether the user agent is running on a mobile device. + * + * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent + * is promoted as the gecko/webkit logic is likely inaccurate. + * + * @type {boolean} + */ +goog.userAgent.MOBILE = + goog.userAgent.ASSUME_MOBILE_WEBKIT || goog.userAgent.isMobile_(); + + +/** + * Used while transitioning code to use WEBKIT instead. + * @type {boolean} + * @deprecated Use {@link goog.userAgent.product.SAFARI} instead. + * TODO(nicksantos): Delete this from goog.userAgent. + */ +goog.userAgent.SAFARI = goog.userAgent.WEBKIT; + + +/** + * @return {string} the platform (operating system) the user agent is running + * on. Default to empty string because navigator.platform may not be defined + * (on Rhino, for example). + * @private + */ +goog.userAgent.determinePlatform_ = function() { + var navigator = goog.userAgent.getNavigator(); + return navigator && navigator.platform || ''; +}; + + +/** + * The platform (operating system) the user agent is running on. Default to + * empty string because navigator.platform may not be defined (on Rhino, for + * example). + * @type {string} + */ +goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_(); + + +/** + * @define {boolean} Whether the user agent is running on a Macintosh operating + * system. + */ +goog.define('goog.userAgent.ASSUME_MAC', false); + + +/** + * @define {boolean} Whether the user agent is running on a Windows operating + * system. + */ +goog.define('goog.userAgent.ASSUME_WINDOWS', false); + + +/** + * @define {boolean} Whether the user agent is running on a Linux operating + * system. + */ +goog.define('goog.userAgent.ASSUME_LINUX', false); + + +/** + * @define {boolean} Whether the user agent is running on a X11 windowing + * system. + */ +goog.define('goog.userAgent.ASSUME_X11', false); + + +/** + * @define {boolean} Whether the user agent is running on Android. + */ +goog.define('goog.userAgent.ASSUME_ANDROID', false); + + +/** + * @define {boolean} Whether the user agent is running on an iPhone. + */ +goog.define('goog.userAgent.ASSUME_IPHONE', false); + + +/** + * @define {boolean} Whether the user agent is running on an iPad. + */ +goog.define('goog.userAgent.ASSUME_IPAD', false); + + +/** + * @define {boolean} Whether the user agent is running on an iPod. + */ +goog.define('goog.userAgent.ASSUME_IPOD', false); + + +/** + * @type {boolean} + * @private + */ +goog.userAgent.PLATFORM_KNOWN_ = goog.userAgent.ASSUME_MAC || + goog.userAgent.ASSUME_WINDOWS || goog.userAgent.ASSUME_LINUX || + goog.userAgent.ASSUME_X11 || goog.userAgent.ASSUME_ANDROID || + goog.userAgent.ASSUME_IPHONE || goog.userAgent.ASSUME_IPAD || + goog.userAgent.ASSUME_IPOD; + + +/** + * Whether the user agent is running on a Macintosh operating system. + * @type {boolean} + */ +goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_MAC : + goog.labs.userAgent.platform.isMacintosh(); + + +/** + * Whether the user agent is running on a Windows operating system. + * @type {boolean} + */ +goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_WINDOWS : + goog.labs.userAgent.platform.isWindows(); + + +/** + * Whether the user agent is Linux per the legacy behavior of + * goog.userAgent.LINUX, which considered ChromeOS to also be + * Linux. + * @return {boolean} + * @private + */ +goog.userAgent.isLegacyLinux_ = function() { + return goog.labs.userAgent.platform.isLinux() || + goog.labs.userAgent.platform.isChromeOS(); +}; + + +/** + * Whether the user agent is running on a Linux operating system. + * + * Note that goog.userAgent.LINUX considers ChromeOS to be Linux, + * while goog.labs.userAgent.platform considers ChromeOS and + * Linux to be different OSes. + * + * @type {boolean} + */ +goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_LINUX : + goog.userAgent.isLegacyLinux_(); + + +/** + * @return {boolean} Whether the user agent is an X11 windowing system. + * @private + */ +goog.userAgent.isX11_ = function() { + var navigator = goog.userAgent.getNavigator(); + return !!navigator && + goog.string.contains(navigator['appVersion'] || '', 'X11'); +}; + + +/** + * Whether the user agent is running on a X11 windowing system. + * @type {boolean} + */ +goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_X11 : + goog.userAgent.isX11_(); + + +/** + * Whether the user agent is running on Android. + * @type {boolean} + */ +goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_ANDROID : + goog.labs.userAgent.platform.isAndroid(); + + +/** + * Whether the user agent is running on an iPhone. + * @type {boolean} + */ +goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_IPHONE : + goog.labs.userAgent.platform.isIphone(); + + +/** + * Whether the user agent is running on an iPad. + * @type {boolean} + */ +goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_IPAD : + goog.labs.userAgent.platform.isIpad(); + + +/** + * Whether the user agent is running on an iPod. + * @type {boolean} + */ +goog.userAgent.IPOD = goog.userAgent.PLATFORM_KNOWN_ ? + goog.userAgent.ASSUME_IPOD : + goog.labs.userAgent.platform.isIpod(); + + +/** + * @return {string} The string that describes the version number of the user + * agent. + * @private + */ +goog.userAgent.determineVersion_ = function() { + // All browsers have different ways to detect the version and they all have + // different naming schemes. + // version is a string rather than a number because it may contain 'b', 'a', + // and so on. + var version = ''; + var arr = goog.userAgent.getVersionRegexResult_(); + if (arr) { + version = arr ? arr[1] : ''; + } + + if (goog.userAgent.IE) { + // IE9 can be in document mode 9 but be reporting an inconsistent user agent + // version. If it is identifying as a version lower than 9 we take the + // documentMode as the version instead. IE8 has similar behavior. + // It is recommended to set the X-UA-Compatible header to ensure that IE9 + // uses documentMode 9. + var docMode = goog.userAgent.getDocumentMode_(); + if (docMode != null && docMode > parseFloat(version)) { + return String(docMode); + } + } + + return version; +}; + + +/** + * @return {?Array|undefined} The version regex matches from parsing the user + * agent string. These regex statements must be executed inline so they can + * be compiled out by the closure compiler with the rest of the useragent + * detection logic when ASSUME_* is specified. + * @private + */ +goog.userAgent.getVersionRegexResult_ = function() { + var userAgent = goog.userAgent.getUserAgentString(); + if (goog.userAgent.GECKO) { + return /rv\:([^\);]+)(\)|;)/.exec(userAgent); + } + if (goog.userAgent.EDGE) { + return /Edge\/([\d\.]+)/.exec(userAgent); + } + if (goog.userAgent.IE) { + return /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(userAgent); + } + if (goog.userAgent.WEBKIT) { + // WebKit/125.4 + return /WebKit\/(\S+)/.exec(userAgent); + } + if (goog.userAgent.OPERA) { + // If none of the above browsers were detected but the browser is Opera, the + // only string that is of interest is 'Version/<number>'. + return /(?:Version)[ \/]?(\S+)/.exec(userAgent); + } + return undefined; +}; + + +/** + * @return {number|undefined} Returns the document mode (for testing). + * @private + */ +goog.userAgent.getDocumentMode_ = function() { + // NOTE(user): goog.userAgent may be used in context where there is no DOM. + var doc = goog.global['document']; + return doc ? doc['documentMode'] : undefined; +}; + + +/** + * The version of the user agent. This is a string because it might contain + * 'b' (as in beta) as well as multiple dots. + * @type {string} + */ +goog.userAgent.VERSION = goog.userAgent.determineVersion_(); + + +/** + * Compares two version numbers. + * + * @param {string} v1 Version of first item. + * @param {string} v2 Version of second item. + * + * @return {number} 1 if first argument is higher + * 0 if arguments are equal + * -1 if second argument is higher. + * @deprecated Use goog.string.compareVersions. + */ +goog.userAgent.compare = function(v1, v2) { + return goog.string.compareVersions(v1, v2); +}; + + +/** + * Cache for {@link goog.userAgent.isVersionOrHigher}. + * Calls to compareVersions are surprisingly expensive and, as a browser's + * version number is unlikely to change during a session, we cache the results. + * @const + * @private + */ +goog.userAgent.isVersionOrHigherCache_ = {}; + + +/** + * Whether the user agent version is higher or the same as the given version. + * NOTE: When checking the version numbers for Firefox and Safari, be sure to + * use the engine's version, not the browser's version number. For example, + * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11. + * Opera and Internet Explorer versions match the product release number.<br> + * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history"> + * Webkit</a> + * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a> + * + * @param {string|number} version The version to check. + * @return {boolean} Whether the user agent version is higher or the same as + * the given version. + */ +goog.userAgent.isVersionOrHigher = function(version) { + return goog.userAgent.ASSUME_ANY_VERSION || + goog.userAgent.isVersionOrHigherCache_[version] || + (goog.userAgent.isVersionOrHigherCache_[version] = + goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0); +}; + + +/** + * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}. + * @param {string|number} version The version to check. + * @return {boolean} Whether the user agent version is higher or the same as + * the given version. + * @deprecated Use goog.userAgent.isVersionOrHigher(). + */ +goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher; + + +/** + * Whether the IE effective document mode is higher or the same as the given + * document mode version. + * NOTE: Only for IE, return false for another browser. + * + * @param {number} documentMode The document mode version to check. + * @return {boolean} Whether the IE effective document mode is higher or the + * same as the given version. + */ +goog.userAgent.isDocumentModeOrHigher = function(documentMode) { + return Number(goog.userAgent.DOCUMENT_MODE) >= documentMode; +}; + + +/** + * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}. + * @param {number} version The version to check. + * @return {boolean} Whether the IE effective document mode is higher or the + * same as the given version. + * @deprecated Use goog.userAgent.isDocumentModeOrHigher(). + */ +goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher; + + +/** + * For IE version < 7, documentMode is undefined, so attempt to use the + * CSS1Compat property to see if we are in standards mode. If we are in + * standards mode, treat the browser version as the document mode. Otherwise, + * IE is emulating version 5. + * @type {number|undefined} + * @const + */ +goog.userAgent.DOCUMENT_MODE = (function() { + var doc = goog.global['document']; + var mode = goog.userAgent.getDocumentMode_(); + if (!doc || !goog.userAgent.IE) { + return undefined; + } + return mode || (doc['compatMode'] == 'CSS1Compat' ? + parseInt(goog.userAgent.VERSION, 10) : + 5); +})(); + +goog.provide('ol.dom'); + +goog.require('goog.asserts'); +goog.require('goog.userAgent'); +goog.require('goog.vec.Mat4'); +goog.require('ol'); + + +/** + * Create an html canvas element and returns its 2d context. + * @param {number=} opt_width Canvas width. + * @param {number=} opt_height Canvas height. + * @return {CanvasRenderingContext2D} The context. + */ +ol.dom.createCanvasContext2D = function(opt_width, opt_height) { + var canvas = document.createElement('CANVAS'); + if (opt_width) { + canvas.width = opt_width; + } + if (opt_height) { + canvas.height = opt_height; + } + return canvas.getContext('2d'); +}; + + +/** + * Detect 2d transform. + * Adapted from http://stackoverflow.com/q/5661671/130442 + * http://caniuse.com/#feat=transforms2d + * @return {boolean} + */ +ol.dom.canUseCssTransform = (function() { + var canUseCssTransform; + return function() { + if (canUseCssTransform === undefined) { + goog.asserts.assert(document.body, + 'document.body should not be null'); + goog.asserts.assert(ol.global.getComputedStyle, + 'getComputedStyle is required (unsupported browser?)'); + + var el = document.createElement('P'), + has2d, + transforms = { + 'webkitTransform': '-webkit-transform', + 'OTransform': '-o-transform', + 'msTransform': '-ms-transform', + 'MozTransform': '-moz-transform', + 'transform': 'transform' + }; + document.body.appendChild(el); + for (var t in transforms) { + if (t in el.style) { + el.style[t] = 'translate(1px,1px)'; + has2d = ol.global.getComputedStyle(el).getPropertyValue( + transforms[t]); + } + } + document.body.removeChild(el); + + canUseCssTransform = (has2d && has2d !== 'none'); + } + return canUseCssTransform; + }; +}()); + + +/** + * Detect 3d transform. + * Adapted from http://stackoverflow.com/q/5661671/130442 + * http://caniuse.com/#feat=transforms3d + * @return {boolean} + */ +ol.dom.canUseCssTransform3D = (function() { + var canUseCssTransform3D; + return function() { + if (canUseCssTransform3D === undefined) { + goog.asserts.assert(document.body, + 'document.body should not be null'); + goog.asserts.assert(ol.global.getComputedStyle, + 'getComputedStyle is required (unsupported browser?)'); + + var el = document.createElement('P'), + has3d, + transforms = { + 'webkitTransform': '-webkit-transform', + 'OTransform': '-o-transform', + 'msTransform': '-ms-transform', + 'MozTransform': '-moz-transform', + 'transform': 'transform' + }; + document.body.appendChild(el); + for (var t in transforms) { + if (t in el.style) { + el.style[t] = 'translate3d(1px,1px,1px)'; + has3d = ol.global.getComputedStyle(el).getPropertyValue( + transforms[t]); + } + } + document.body.removeChild(el); + + canUseCssTransform3D = (has3d && has3d !== 'none'); + } + return canUseCssTransform3D; + }; +}()); + + +/** + * @param {Element} element Element. + * @param {string} value Value. + */ +ol.dom.setTransform = function(element, value) { + var style = element.style; + style.WebkitTransform = value; + style.MozTransform = value; + style.OTransform = value; + style.msTransform = value; + style.transform = value; + + // IE 9+ seems to assume transform-origin: 100% 100%; for some unknown reason + if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('9.0')) { + element.style.transformOrigin = '0 0'; + } +}; + + +/** + * @param {!Element} element Element. + * @param {goog.vec.Mat4.Number} transform Matrix. + * @param {number=} opt_precision Precision. + */ +ol.dom.transformElement2D = function(element, transform, opt_precision) { + // using matrix() causes gaps in Chrome and Firefox on Mac OS X, so prefer + // matrix3d() + var i; + if (ol.dom.canUseCssTransform3D()) { + var value3D; + + if (opt_precision !== undefined) { + /** @type {Array.<string>} */ + var strings3D = new Array(16); + for (i = 0; i < 16; ++i) { + strings3D[i] = transform[i].toFixed(opt_precision); + } + value3D = strings3D.join(','); + } else { + value3D = transform.join(','); + } + ol.dom.setTransform(element, 'matrix3d(' + value3D + ')'); + } else if (ol.dom.canUseCssTransform()) { + /** @type {Array.<number>} */ + var transform2D = [ + goog.vec.Mat4.getElement(transform, 0, 0), + goog.vec.Mat4.getElement(transform, 1, 0), + goog.vec.Mat4.getElement(transform, 0, 1), + goog.vec.Mat4.getElement(transform, 1, 1), + goog.vec.Mat4.getElement(transform, 0, 3), + goog.vec.Mat4.getElement(transform, 1, 3) + ]; + var value2D; + if (opt_precision !== undefined) { + /** @type {Array.<string>} */ + var strings2D = new Array(6); + for (i = 0; i < 6; ++i) { + strings2D[i] = transform2D[i].toFixed(opt_precision); + } + value2D = strings2D.join(','); + } else { + value2D = transform2D.join(','); + } + ol.dom.setTransform(element, 'matrix(' + value2D + ')'); + } else { + element.style.left = + Math.round(goog.vec.Mat4.getElement(transform, 0, 3)) + 'px'; + element.style.top = + Math.round(goog.vec.Mat4.getElement(transform, 1, 3)) + 'px'; + + // TODO: Add scaling here. This isn't quite as simple as multiplying + // width/height, because that only changes the container size, not the + // content size. + } +}; + + +/** + * Get the current computed width for the given element including margin, + * padding and border. + * Equivalent to jQuery's `$(el).outerWidth(true)`. + * @param {!Element} element Element. + * @return {number} The width. + */ +ol.dom.outerWidth = function(element) { + var width = element.offsetWidth; + var style = element.currentStyle || ol.global.getComputedStyle(element); + width += parseInt(style.marginLeft, 10) + parseInt(style.marginRight, 10); + + return width; +}; + + +/** + * Get the current computed height for the given element including margin, + * padding and border. + * Equivalent to jQuery's `$(el).outerHeight(true)`. + * @param {!Element} element Element. + * @return {number} The height. + */ +ol.dom.outerHeight = function(element) { + var height = element.offsetHeight; + var style = element.currentStyle || ol.global.getComputedStyle(element); + height += parseInt(style.marginTop, 10) + parseInt(style.marginBottom, 10); + + return height; +}; + +/** + * @param {Node} newNode Node to replace old node + * @param {Node} oldNode The node to be replaced + */ +ol.dom.replaceNode = function(newNode, oldNode) { + var parent = oldNode.parentNode; + if (parent) { + parent.replaceChild(newNode, oldNode); + } +}; + +/** + * @param {Node} node The node to remove. + * @returns {Node} The node that was removed or null. + */ +ol.dom.removeNode = function(node) { + return node && node.parentNode ? node.parentNode.removeChild(node) : null; +}; + +/** + * @param {Node} node The node to remove the children from. + */ +ol.dom.removeChildren = function(node) { + while (node.lastChild) { + node.removeChild(node.lastChild); + } +}; + +goog.provide('ol.MapEvent'); +goog.provide('ol.MapEventType'); + +goog.require('ol.events.Event'); + + +/** + * @enum {string} + */ +ol.MapEventType = { + + /** + * Triggered after a map frame is rendered. + * @event ol.MapEvent#postrender + * @api + */ + POSTRENDER: 'postrender', + + /** + * Triggered after the map is moved. + * @event ol.MapEvent#moveend + * @api stable + */ + MOVEEND: 'moveend' + +}; + + +/** + * @classdesc + * Events emitted as map events are instances of this type. + * See {@link ol.Map} for which events trigger a map event. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.MapEvent} + * @param {string} type Event type. + * @param {ol.Map} map Map. + * @param {?olx.FrameState=} opt_frameState Frame state. + */ +ol.MapEvent = function(type, map, opt_frameState) { + + ol.events.Event.call(this, type); + + /** + * The map where the event occurred. + * @type {ol.Map} + * @api stable + */ + this.map = map; + + /** + * The frame state at the time of the event. + * @type {?olx.FrameState} + * @api + */ + this.frameState = opt_frameState !== undefined ? opt_frameState : null; + +}; +ol.inherits(ol.MapEvent, ol.events.Event); + +goog.provide('ol.control.Control'); + +goog.require('ol.events'); +goog.require('ol'); +goog.require('ol.MapEventType'); +goog.require('ol.Object'); +goog.require('ol.dom'); + + +/** + * @classdesc + * A control is a visible widget with a DOM element in a fixed position on the + * screen. They can involve user input (buttons), or be informational only; + * the position is determined using CSS. By default these are placed in the + * container with CSS class name `ol-overlaycontainer-stopevent`, but can use + * any outside DOM element. + * + * This is the base class for controls. You can use it for simple custom + * controls by creating the element with listeners, creating an instance: + * ```js + * var myControl = new ol.control.Control({element: myElement}); + * ``` + * and then adding this to the map. + * + * The main advantage of having this as a control rather than a simple separate + * DOM element is that preventing propagation is handled for you. Controls + * will also be `ol.Object`s in a `ol.Collection`, so you can use their + * methods. + * + * You can also extend this base for your own control class. See + * examples/custom-controls for an example of how to do this. + * + * @constructor + * @extends {ol.Object} + * @implements {oli.control.Control} + * @param {olx.control.ControlOptions} options Control options. + * @api stable + */ +ol.control.Control = function(options) { + + ol.Object.call(this); + + /** + * @protected + * @type {Element} + */ + this.element = options.element ? options.element : null; + + /** + * @private + * @type {Element} + */ + this.target_ = null; + + /** + * @private + * @type {ol.Map} + */ + this.map_ = null; + + /** + * @protected + * @type {!Array.<ol.EventsKey>} + */ + this.listenerKeys = []; + + /** + * @type {function(ol.MapEvent)} + */ + this.render = options.render ? options.render : ol.nullFunction; + + if (options.target) { + this.setTarget(options.target); + } + +}; +ol.inherits(ol.control.Control, ol.Object); + + +/** + * @inheritDoc + */ +ol.control.Control.prototype.disposeInternal = function() { + ol.dom.removeNode(this.element); + ol.Object.prototype.disposeInternal.call(this); +}; + + +/** + * Get the map associated with this control. + * @return {ol.Map} Map. + * @api stable + */ +ol.control.Control.prototype.getMap = function() { + return this.map_; +}; + + +/** + * Remove the control from its current map and attach it to the new map. + * Subclasses may set up event handlers to get notified about changes to + * the map here. + * @param {ol.Map} map Map. + * @api stable + */ +ol.control.Control.prototype.setMap = function(map) { + if (this.map_) { + ol.dom.removeNode(this.element); + } + for (var i = 0, ii = this.listenerKeys.length; i < ii; ++i) { + ol.events.unlistenByKey(this.listenerKeys[i]); + } + this.listenerKeys.length = 0; + this.map_ = map; + if (this.map_) { + var target = this.target_ ? + this.target_ : map.getOverlayContainerStopEvent(); + target.appendChild(this.element); + if (this.render !== ol.nullFunction) { + this.listenerKeys.push(ol.events.listen(map, + ol.MapEventType.POSTRENDER, this.render, this)); + } + map.render(); + } +}; + + +/** + * This function is used to set a target element for the control. It has no + * effect if it is called after the control has been added to the map (i.e. + * after `setMap` is called on the control). If no `target` is set in the + * options passed to the control constructor and if `setTarget` is not called + * then the control is added to the map's overlay container. + * @param {Element|string} target Target. + * @api + */ +ol.control.Control.prototype.setTarget = function(target) { + this.target_ = typeof target === 'string' ? + document.getElementById(target) : + target; +}; + +goog.provide('ol.css'); + + +/** + * The CSS class for hidden feature. + * + * @const + * @type {string} + */ +ol.css.CLASS_HIDDEN = 'ol-hidden'; + + +/** + * The CSS class that we'll give the DOM elements to have them unselectable. + * + * @const + * @type {string} + */ +ol.css.CLASS_UNSELECTABLE = 'ol-unselectable'; + + +/** + * The CSS class for unsupported feature. + * + * @const + * @type {string} + */ +ol.css.CLASS_UNSUPPORTED = 'ol-unsupported'; + + +/** + * The CSS class for controls. + * + * @const + * @type {string} + */ +ol.css.CLASS_CONTROL = 'ol-control'; + +goog.provide('ol.structs.LRUCache'); + +goog.require('goog.asserts'); +goog.require('ol.object'); + + +/** + * Implements a Least-Recently-Used cache where the keys do not conflict with + * Object's properties (e.g. 'hasOwnProperty' is not allowed as a key). Expiring + * items from the cache is the responsibility of the user. + * @constructor + * @struct + * @template T + */ +ol.structs.LRUCache = function() { + + /** + * @private + * @type {number} + */ + this.count_ = 0; + + /** + * @private + * @type {!Object.<string, ol.LRUCacheEntry>} + */ + this.entries_ = {}; + + /** + * @private + * @type {?ol.LRUCacheEntry} + */ + this.oldest_ = null; + + /** + * @private + * @type {?ol.LRUCacheEntry} + */ + this.newest_ = null; + +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.structs.LRUCache.prototype.assertValid = function() { + if (this.count_ === 0) { + goog.asserts.assert(ol.object.isEmpty(this.entries_), + 'entries must be an empty object (count = 0)'); + goog.asserts.assert(!this.oldest_, + 'oldest must be null (count = 0)'); + goog.asserts.assert(!this.newest_, + 'newest must be null (count = 0)'); + } else { + goog.asserts.assert(Object.keys(this.entries_).length == this.count_, + 'number of entries matches count'); + goog.asserts.assert(this.oldest_, + 'we have an oldest entry'); + goog.asserts.assert(!this.oldest_.older, + 'no entry is older than oldest'); + goog.asserts.assert(this.newest_, + 'we have a newest entry'); + goog.asserts.assert(!this.newest_.newer, + 'no entry is newer than newest'); + var i, entry; + var older = null; + i = 0; + for (entry = this.oldest_; entry; entry = entry.newer) { + goog.asserts.assert(entry.older === older, + 'entry.older links to correct older'); + older = entry; + ++i; + } + goog.asserts.assert(i == this.count_, 'iterated correct amount of times'); + var newer = null; + i = 0; + for (entry = this.newest_; entry; entry = entry.older) { + goog.asserts.assert(entry.newer === newer, + 'entry.newer links to correct newer'); + newer = entry; + ++i; + } + goog.asserts.assert(i == this.count_, 'iterated correct amount of times'); + } +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.structs.LRUCache.prototype.clear = function() { + this.count_ = 0; + this.entries_ = {}; + this.oldest_ = null; + this.newest_ = null; +}; + + +/** + * @param {string} key Key. + * @return {boolean} Contains key. + */ +ol.structs.LRUCache.prototype.containsKey = function(key) { + return this.entries_.hasOwnProperty(key); +}; + + +/** + * @param {function(this: S, T, string, ol.structs.LRUCache): ?} f The function + * to call for every entry from the oldest to the newer. This function takes + * 3 arguments (the entry value, the entry key and the LRUCache object). + * The return value is ignored. + * @param {S=} opt_this The object to use as `this` in `f`. + * @template S + */ +ol.structs.LRUCache.prototype.forEach = function(f, opt_this) { + var entry = this.oldest_; + while (entry) { + f.call(opt_this, entry.value_, entry.key_, this); + entry = entry.newer; + } +}; + + +/** + * @param {string} key Key. + * @return {T} Value. + */ +ol.structs.LRUCache.prototype.get = function(key) { + var entry = this.entries_[key]; + goog.asserts.assert(entry !== undefined, 'an entry exists for key %s', key); + if (entry === this.newest_) { + return entry.value_; + } else if (entry === this.oldest_) { + this.oldest_ = /** @type {ol.LRUCacheEntry} */ (this.oldest_.newer); + this.oldest_.older = null; + } else { + entry.newer.older = entry.older; + entry.older.newer = entry.newer; + } + entry.newer = null; + entry.older = this.newest_; + this.newest_.newer = entry; + this.newest_ = entry; + return entry.value_; +}; + + +/** + * @return {number} Count. + */ +ol.structs.LRUCache.prototype.getCount = function() { + return this.count_; +}; + + +/** + * @return {Array.<string>} Keys. + */ +ol.structs.LRUCache.prototype.getKeys = function() { + var keys = new Array(this.count_); + var i = 0; + var entry; + for (entry = this.newest_; entry; entry = entry.older) { + keys[i++] = entry.key_; + } + goog.asserts.assert(i == this.count_, 'iterated correct number of times'); + return keys; +}; + + +/** + * @return {Array.<T>} Values. + */ +ol.structs.LRUCache.prototype.getValues = function() { + var values = new Array(this.count_); + var i = 0; + var entry; + for (entry = this.newest_; entry; entry = entry.older) { + values[i++] = entry.value_; + } + goog.asserts.assert(i == this.count_, 'iterated correct number of times'); + return values; +}; + + +/** + * @return {T} Last value. + */ +ol.structs.LRUCache.prototype.peekLast = function() { + goog.asserts.assert(this.oldest_, 'oldest must not be null'); + return this.oldest_.value_; +}; + + +/** + * @return {string} Last key. + */ +ol.structs.LRUCache.prototype.peekLastKey = function() { + goog.asserts.assert(this.oldest_, 'oldest must not be null'); + return this.oldest_.key_; +}; + + +/** + * @return {T} value Value. + */ +ol.structs.LRUCache.prototype.pop = function() { + goog.asserts.assert(this.oldest_, 'oldest must not be null'); + goog.asserts.assert(this.newest_, 'newest must not be null'); + var entry = this.oldest_; + goog.asserts.assert(entry.key_ in this.entries_, + 'oldest is indexed in entries'); + delete this.entries_[entry.key_]; + if (entry.newer) { + entry.newer.older = null; + } + this.oldest_ = /** @type {ol.LRUCacheEntry} */ (entry.newer); + if (!this.oldest_) { + this.newest_ = null; + } + --this.count_; + return entry.value_; +}; + + +/** + * @param {string} key Key. + * @param {T} value Value. + */ +ol.structs.LRUCache.prototype.replace = function(key, value) { + this.get(key); // update `newest_` + this.entries_[key].value_ = value; +}; + + +/** + * @param {string} key Key. + * @param {T} value Value. + */ +ol.structs.LRUCache.prototype.set = function(key, value) { + goog.asserts.assert(!(key in {}), + 'key is not a standard property of objects (e.g. "__proto__")'); + goog.asserts.assert(!(key in this.entries_), + 'key is not used already'); + var entry = /** @type {ol.LRUCacheEntry} */ ({ + key_: key, + newer: null, + older: this.newest_, + value_: value + }); + if (!this.newest_) { + this.oldest_ = entry; + } else { + this.newest_.newer = entry; + } + this.newest_ = entry; + this.entries_[key] = entry; + ++this.count_; +}; + +goog.provide('ol.tilecoord'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); + + +/** + * @enum {number} + */ +ol.QuadKeyCharCode = { + ZERO: '0'.charCodeAt(0), + ONE: '1'.charCodeAt(0), + TWO: '2'.charCodeAt(0), + THREE: '3'.charCodeAt(0) +}; + + +/** + * @param {string} str String that follows pattern “z/x/y†where x, y and z are + * numbers. + * @return {ol.TileCoord} Tile coord. + */ +ol.tilecoord.createFromString = function(str) { + var v = str.split('/'); + goog.asserts.assert(v.length === 3, + 'must provide a string in "z/x/y" format, got "%s"', str); + return v.map(function(e) { + return parseInt(e, 10); + }); +}; + + +/** + * @param {number} z Z. + * @param {number} x X. + * @param {number} y Y. + * @param {ol.TileCoord=} opt_tileCoord Tile coordinate. + * @return {ol.TileCoord} Tile coordinate. + */ +ol.tilecoord.createOrUpdate = function(z, x, y, opt_tileCoord) { + if (opt_tileCoord !== undefined) { + opt_tileCoord[0] = z; + opt_tileCoord[1] = x; + opt_tileCoord[2] = y; + return opt_tileCoord; + } else { + return [z, x, y]; + } +}; + + +/** + * @param {number} z Z. + * @param {number} x X. + * @param {number} y Y. + * @return {string} Key. + */ +ol.tilecoord.getKeyZXY = function(z, x, y) { + return z + '/' + x + '/' + y; +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coord. + * @return {number} Hash. + */ +ol.tilecoord.hash = function(tileCoord) { + return (tileCoord[1] << tileCoord[0]) + tileCoord[2]; +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coord. + * @return {string} Quad key. + */ +ol.tilecoord.quadKey = function(tileCoord) { + var z = tileCoord[0]; + var digits = new Array(z); + var mask = 1 << (z - 1); + var i, charCode; + for (i = 0; i < z; ++i) { + charCode = ol.QuadKeyCharCode.ZERO; + if (tileCoord[1] & mask) { + charCode += 1; + } + if (tileCoord[2] & mask) { + charCode += 2; + } + digits[i] = String.fromCharCode(charCode); + mask >>= 1; + } + return digits.join(''); +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @param {ol.proj.Projection} projection Projection. + * @return {ol.TileCoord} Tile coordinate. + */ +ol.tilecoord.wrapX = function(tileCoord, tileGrid, projection) { + var z = tileCoord[0]; + var center = tileGrid.getTileCoordCenter(tileCoord); + var projectionExtent = ol.tilegrid.extentFromProjection(projection); + if (!ol.extent.containsCoordinate(projectionExtent, center)) { + var worldWidth = ol.extent.getWidth(projectionExtent); + var worldsAway = Math.ceil((projectionExtent[0] - center[0]) / worldWidth); + center[0] += worldWidth * worldsAway; + return tileGrid.getTileCoordForCoordAndZ(center, z); + } else { + return tileCoord; + } +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {!ol.tilegrid.TileGrid} tileGrid Tile grid. + * @return {boolean} Tile coordinate is within extent and zoom level range. + */ +ol.tilecoord.withinExtentAndZ = function(tileCoord, tileGrid) { + var z = tileCoord[0]; + var x = tileCoord[1]; + var y = tileCoord[2]; + + if (tileGrid.getMinZoom() > z || z > tileGrid.getMaxZoom()) { + return false; + } + var extent = tileGrid.getExtent(); + var tileRange; + if (!extent) { + tileRange = tileGrid.getFullTileRange(z); + } else { + tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + } + if (!tileRange) { + return true; + } else { + return tileRange.containsXY(x, y); + } +}; + +goog.provide('ol.TileCache'); + +goog.require('ol.TileRange'); +goog.require('ol.structs.LRUCache'); +goog.require('ol.tilecoord'); + + +/** + * @constructor + * @extends {ol.structs.LRUCache.<ol.Tile>} + * @param {number=} opt_highWaterMark High water mark. + * @struct + */ +ol.TileCache = function(opt_highWaterMark) { + + ol.structs.LRUCache.call(this); + + /** + * @private + * @type {number} + */ + this.highWaterMark_ = opt_highWaterMark !== undefined ? opt_highWaterMark : 2048; + +}; +ol.inherits(ol.TileCache, ol.structs.LRUCache); + + +/** + * @return {boolean} Can expire cache. + */ +ol.TileCache.prototype.canExpireCache = function() { + return this.getCount() > this.highWaterMark_; +}; + + +/** + * @param {Object.<string, ol.TileRange>} usedTiles Used tiles. + */ +ol.TileCache.prototype.expireCache = function(usedTiles) { + var tile, zKey; + while (this.canExpireCache()) { + tile = this.peekLast(); + zKey = tile.tileCoord[0].toString(); + if (zKey in usedTiles && usedTiles[zKey].contains(tile.tileCoord)) { + break; + } else { + this.pop().dispose(); + } + } +}; + + +/** + * Remove a tile range from the cache, e.g. to invalidate tiles. + * @param {ol.TileRange} tileRange The tile range to prune. + */ +ol.TileCache.prototype.pruneTileRange = function(tileRange) { + var i = this.getCount(), + key; + while (i--) { + key = this.peekLastKey(); + if (tileRange.contains(ol.tilecoord.createFromString(key))) { + this.pop().dispose(); + } else { + this.get(key); + } + } +}; + +goog.provide('ol.Tile'); +goog.provide('ol.TileState'); + +goog.require('ol.events'); +goog.require('ol.events.EventTarget'); +goog.require('ol.events.EventType'); + + +/** + * @enum {number} + */ +ol.TileState = { + IDLE: 0, + LOADING: 1, + LOADED: 2, + ERROR: 3, + EMPTY: 4, + ABORT: 5 +}; + + +/** + * @classdesc + * Base class for tiles. + * + * @constructor + * @extends {ol.events.EventTarget} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.TileState} state State. + */ +ol.Tile = function(tileCoord, state) { + + ol.events.EventTarget.call(this); + + /** + * @type {ol.TileCoord} + */ + this.tileCoord = tileCoord; + + /** + * @protected + * @type {ol.TileState} + */ + this.state = state; + + /** + * An "interim" tile for this tile. The interim tile may be used while this + * one is loading, for "smooth" transitions when changing params/dimensions + * on the source. + * @type {ol.Tile} + */ + this.interimTile = null; + + /** + * A key assigned to the tile. This is used by the tile source to determine + * if this tile can effectively be used, or if a new tile should be created + * and this one be used as an interim tile for this new tile. + * @type {string} + */ + this.key = ''; + +}; +ol.inherits(ol.Tile, ol.events.EventTarget); + + +/** + * @protected + */ +ol.Tile.prototype.changed = function() { + this.dispatchEvent(ol.events.EventType.CHANGE); +}; + + +/** + * Get the HTML image element for this tile (may be a Canvas, Image, or Video). + * @function + * @param {Object=} opt_context Object. + * @return {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} Image. + */ +ol.Tile.prototype.getImage = goog.abstractMethod; + + +/** + * @return {string} Key. + */ +ol.Tile.prototype.getKey = function() { + return goog.getUid(this).toString(); +}; + + +/** + * Get the tile coordinate for this tile. + * @return {ol.TileCoord} The tile coordinate. + * @api + */ +ol.Tile.prototype.getTileCoord = function() { + return this.tileCoord; +}; + + +/** + * @return {ol.TileState} State. + */ +ol.Tile.prototype.getState = function() { + return this.state; +}; + + +/** + * Load the image or retry if loading previously failed. + * Loading is taken care of by the tile queue, and calling this method is + * only needed for preloading or for reloading in case of an error. + * @api + */ +ol.Tile.prototype.load = goog.abstractMethod; + +goog.provide('ol.size'); + + +goog.require('goog.asserts'); + + +/** + * Returns a buffered size. + * @param {ol.Size} size Size. + * @param {number} buffer Buffer. + * @param {ol.Size=} opt_size Optional reusable size array. + * @return {ol.Size} The buffered size. + */ +ol.size.buffer = function(size, buffer, opt_size) { + if (opt_size === undefined) { + opt_size = [0, 0]; + } + opt_size[0] = size[0] + 2 * buffer; + opt_size[1] = size[1] + 2 * buffer; + return opt_size; +}; + + +/** + * Compares sizes for equality. + * @param {ol.Size} a Size. + * @param {ol.Size} b Size. + * @return {boolean} Equals. + */ +ol.size.equals = function(a, b) { + return a[0] == b[0] && a[1] == b[1]; +}; + + +/** + * Determines if a size has a positive area. + * @param {ol.Size} size The size to test. + * @return {boolean} The size has a positive area. + */ +ol.size.hasArea = function(size) { + return size[0] > 0 && size[1] > 0; +}; + + +/** + * Returns a size scaled by a ratio. The result will be an array of integers. + * @param {ol.Size} size Size. + * @param {number} ratio Ratio. + * @param {ol.Size=} opt_size Optional reusable size array. + * @return {ol.Size} The scaled size. + */ +ol.size.scale = function(size, ratio, opt_size) { + if (opt_size === undefined) { + opt_size = [0, 0]; + } + opt_size[0] = (size[0] * ratio + 0.5) | 0; + opt_size[1] = (size[1] * ratio + 0.5) | 0; + return opt_size; +}; + + +/** + * Returns an `ol.Size` array for the passed in number (meaning: square) or + * `ol.Size` array. + * (meaning: non-square), + * @param {number|ol.Size} size Width and height. + * @param {ol.Size=} opt_size Optional reusable size array. + * @return {ol.Size} Size. + * @api stable + */ +ol.size.toSize = function(size, opt_size) { + if (Array.isArray(size)) { + return size; + } else { + goog.asserts.assert(goog.isNumber(size)); + if (opt_size === undefined) { + opt_size = [size, size]; + } else { + opt_size[0] = size; + opt_size[1] = size; + } + return opt_size; + } +}; + +goog.provide('ol.source.Source'); +goog.provide('ol.source.State'); + +goog.require('ol'); +goog.require('ol.Attribution'); +goog.require('ol.Object'); +goog.require('ol.proj'); + + +/** + * State of the source, one of 'undefined', 'loading', 'ready' or 'error'. + * @enum {string} + */ +ol.source.State = { + UNDEFINED: 'undefined', + LOADING: 'loading', + READY: 'ready', + ERROR: 'error' +}; + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for {@link ol.layer.Layer} sources. + * + * A generic `change` event is triggered when the state of the source changes. + * + * @constructor + * @extends {ol.Object} + * @param {ol.SourceSourceOptions} options Source options. + * @api stable + */ +ol.source.Source = function(options) { + + ol.Object.call(this); + + /** + * @private + * @type {ol.proj.Projection} + */ + this.projection_ = ol.proj.get(options.projection); + + /** + * @private + * @type {Array.<ol.Attribution>} + */ + this.attributions_ = ol.source.Source.toAttributionsArray_(options.attributions); + + /** + * @private + * @type {string|olx.LogoOptions|undefined} + */ + this.logo_ = options.logo; + + /** + * @private + * @type {ol.source.State} + */ + this.state_ = options.state !== undefined ? + options.state : ol.source.State.READY; + + /** + * @private + * @type {boolean} + */ + this.wrapX_ = options.wrapX !== undefined ? options.wrapX : false; + +}; +ol.inherits(ol.source.Source, ol.Object); + +/** + * Turns various ways of defining an attribution to an array of `ol.Attributions`. + * + * @param {ol.AttributionLike|undefined} + * attributionLike The attributions as string, array of strings, + * `ol.Attribution`, array of `ol.Attribution` or undefined. + * @return {Array.<ol.Attribution>} The array of `ol.Attribution` or null if + * `undefined` was given. + */ +ol.source.Source.toAttributionsArray_ = function(attributionLike) { + if (typeof attributionLike === 'string') { + return [new ol.Attribution({html: attributionLike})]; + } else if (attributionLike instanceof ol.Attribution) { + return [attributionLike]; + } else if (Array.isArray(attributionLike)) { + var len = attributionLike.length; + var attributions = new Array(len); + for (var i = 0; i < len; i++) { + var item = attributionLike[i]; + if (typeof item === 'string') { + attributions[i] = new ol.Attribution({html: item}); + } else { + attributions[i] = item; + } + } + return attributions; + } else { + return null; + } +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {Object.<string, boolean>} skippedFeatureUids Skipped feature uids. + * @param {function((ol.Feature|ol.render.Feature)): T} callback Feature + * callback. + * @return {T|undefined} Callback result. + * @template T + */ +ol.source.Source.prototype.forEachFeatureAtCoordinate = ol.nullFunction; + + +/** + * Get the attributions of the source. + * @return {Array.<ol.Attribution>} Attributions. + * @api stable + */ +ol.source.Source.prototype.getAttributions = function() { + return this.attributions_; +}; + + +/** + * Get the logo of the source. + * @return {string|olx.LogoOptions|undefined} Logo. + * @api stable + */ +ol.source.Source.prototype.getLogo = function() { + return this.logo_; +}; + + +/** + * Get the projection of the source. + * @return {ol.proj.Projection} Projection. + * @api + */ +ol.source.Source.prototype.getProjection = function() { + return this.projection_; +}; + + +/** + * @return {Array.<number>|undefined} Resolutions. + */ +ol.source.Source.prototype.getResolutions = goog.abstractMethod; + + +/** + * Get the state of the source, see {@link ol.source.State} for possible states. + * @return {ol.source.State} State. + * @api + */ +ol.source.Source.prototype.getState = function() { + return this.state_; +}; + + +/** + * @return {boolean|undefined} Wrap X. + */ +ol.source.Source.prototype.getWrapX = function() { + return this.wrapX_; +}; + + +/** + * Refreshes the source and finally dispatches a 'change' event. + * @api + */ +ol.source.Source.prototype.refresh = function() { + this.changed(); +}; + + +/** + * Set the attributions of the source. + * @param {ol.AttributionLike|undefined} attributions Attributions. + * Can be passed as `string`, `Array<string>`, `{@link ol.Attribution}`, + * `Array<{@link ol.Attribution}>` or `undefined`. + * @api + */ +ol.source.Source.prototype.setAttributions = function(attributions) { + this.attributions_ = ol.source.Source.toAttributionsArray_(attributions); + this.changed(); +}; + + +/** + * Set the logo of the source. + * @param {string|olx.LogoOptions|undefined} logo Logo. + */ +ol.source.Source.prototype.setLogo = function(logo) { + this.logo_ = logo; +}; + + +/** + * Set the state of the source. + * @param {ol.source.State} state State. + * @protected + */ +ol.source.Source.prototype.setState = function(state) { + this.state_ = state; + this.changed(); +}; + +goog.provide('ol.tilegrid.TileGrid'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.TileRange'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.extent.Corner'); +goog.require('ol.math'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.proj.METERS_PER_UNIT'); +goog.require('ol.proj.Projection'); +goog.require('ol.proj.Units'); +goog.require('ol.size'); +goog.require('ol.tilecoord'); + + +/** + * @classdesc + * Base class for setting the grid pattern for sources accessing tiled-image + * servers. + * + * @constructor + * @param {olx.tilegrid.TileGridOptions} options Tile grid options. + * @struct + * @api stable + */ +ol.tilegrid.TileGrid = function(options) { + + /** + * @protected + * @type {number} + */ + this.minZoom = options.minZoom !== undefined ? options.minZoom : 0; + + /** + * @private + * @type {!Array.<number>} + */ + this.resolutions_ = options.resolutions; + goog.asserts.assert(ol.array.isSorted(this.resolutions_, function(a, b) { + return b - a; + }, true), 'resolutions must be sorted in descending order'); + + /** + * @protected + * @type {number} + */ + this.maxZoom = this.resolutions_.length - 1; + + /** + * @private + * @type {ol.Coordinate} + */ + this.origin_ = options.origin !== undefined ? options.origin : null; + + /** + * @private + * @type {Array.<ol.Coordinate>} + */ + this.origins_ = null; + if (options.origins !== undefined) { + this.origins_ = options.origins; + goog.asserts.assert(this.origins_.length == this.resolutions_.length, + 'number of origins and resolutions must be equal'); + } + + var extent = options.extent; + + if (extent !== undefined && + !this.origin_ && !this.origins_) { + this.origin_ = ol.extent.getTopLeft(extent); + } + + goog.asserts.assert( + (!this.origin_ && this.origins_) || + (this.origin_ && !this.origins_), + 'either origin or origins must be configured, never both'); + + /** + * @private + * @type {Array.<number|ol.Size>} + */ + this.tileSizes_ = null; + if (options.tileSizes !== undefined) { + this.tileSizes_ = options.tileSizes; + goog.asserts.assert(this.tileSizes_.length == this.resolutions_.length, + 'number of tileSizes and resolutions must be equal'); + } + + /** + * @private + * @type {number|ol.Size} + */ + this.tileSize_ = options.tileSize !== undefined ? + options.tileSize : + !this.tileSizes_ ? ol.DEFAULT_TILE_SIZE : null; + goog.asserts.assert( + (!this.tileSize_ && this.tileSizes_) || + (this.tileSize_ && !this.tileSizes_), + 'either tileSize or tileSizes must be configured, never both'); + + /** + * @private + * @type {ol.Extent} + */ + this.extent_ = extent !== undefined ? extent : null; + + + /** + * @private + * @type {Array.<ol.TileRange>} + */ + this.fullTileRanges_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.tmpSize_ = [0, 0]; + + if (options.sizes !== undefined) { + goog.asserts.assert(options.sizes.length == this.resolutions_.length, + 'number of sizes and resolutions must be equal'); + this.fullTileRanges_ = options.sizes.map(function(size, z) { + goog.asserts.assert(size[0] !== 0, 'width must not be 0'); + goog.asserts.assert(size[1] !== 0, 'height must not be 0'); + var tileRange = new ol.TileRange( + Math.min(0, size[0]), Math.max(size[0] - 1, -1), + Math.min(0, size[1]), Math.max(size[1] - 1, -1)); + if (this.minZoom <= z && z <= this.maxZoom && extent !== undefined) { + goog.asserts.assert(tileRange.containsTileRange( + this.getTileRangeForExtentAndZ(extent, z)), + 'extent tile range must not exceed tilegrid width and height'); + } + return tileRange; + }, this); + } else if (extent) { + this.calculateTileRanges_(extent); + } + +}; + + +/** + * @private + * @type {ol.TileCoord} + */ +ol.tilegrid.TileGrid.tmpTileCoord_ = [0, 0, 0]; + + +/** + * Call a function with each tile coordinate for a given extent and zoom level. + * + * @param {ol.Extent} extent Extent. + * @param {number} zoom Zoom level. + * @param {function(ol.TileCoord)} callback Function called with each tile coordinate. + * @api + */ +ol.tilegrid.TileGrid.prototype.forEachTileCoord = function(extent, zoom, callback) { + var tileRange = this.getTileRangeForExtentAndZ(extent, zoom); + for (var i = tileRange.minX, ii = tileRange.maxX; i <= ii; ++i) { + for (var j = tileRange.minY, jj = tileRange.maxY; j <= jj; ++j) { + callback([zoom, i, j]); + } + } +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {function(this: T, number, ol.TileRange): boolean} callback Callback. + * @param {T=} opt_this The object to use as `this` in `callback`. + * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object. + * @param {ol.Extent=} opt_extent Temporary ol.Extent object. + * @return {boolean} Callback succeeded. + * @template T + */ +ol.tilegrid.TileGrid.prototype.forEachTileCoordParentTileRange = function(tileCoord, callback, opt_this, opt_tileRange, opt_extent) { + var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); + var z = tileCoord[0] - 1; + while (z >= this.minZoom) { + if (callback.call(opt_this, z, + this.getTileRangeForExtentAndZ(tileCoordExtent, z, opt_tileRange))) { + return true; + } + --z; + } + return false; +}; + + +/** + * Get the extent for this tile grid, if it was configured. + * @return {ol.Extent} Extent. + */ +ol.tilegrid.TileGrid.prototype.getExtent = function() { + return this.extent_; +}; + + +/** + * Get the maximum zoom level for the grid. + * @return {number} Max zoom. + * @api + */ +ol.tilegrid.TileGrid.prototype.getMaxZoom = function() { + return this.maxZoom; +}; + + +/** + * Get the minimum zoom level for the grid. + * @return {number} Min zoom. + * @api + */ +ol.tilegrid.TileGrid.prototype.getMinZoom = function() { + return this.minZoom; +}; + + +/** + * Get the origin for the grid at the given zoom level. + * @param {number} z Z. + * @return {ol.Coordinate} Origin. + * @api stable + */ +ol.tilegrid.TileGrid.prototype.getOrigin = function(z) { + if (this.origin_) { + return this.origin_; + } else { + goog.asserts.assert(this.origins_, + 'origins cannot be null if origin is null'); + goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom, + 'given z is not in allowed range (%s <= %s <= %s)', + this.minZoom, z, this.maxZoom); + return this.origins_[z]; + } +}; + + +/** + * Get the resolution for the given zoom level. + * @param {number} z Z. + * @return {number} Resolution. + * @api stable + */ +ol.tilegrid.TileGrid.prototype.getResolution = function(z) { + goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom, + 'given z is not in allowed range (%s <= %s <= %s)', + this.minZoom, z, this.maxZoom); + return this.resolutions_[z]; +}; + + +/** + * Get the list of resolutions for the tile grid. + * @return {Array.<number>} Resolutions. + * @api stable + */ +ol.tilegrid.TileGrid.prototype.getResolutions = function() { + return this.resolutions_; +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object. + * @param {ol.Extent=} opt_extent Temporary ol.Extent object. + * @return {ol.TileRange} Tile range. + */ +ol.tilegrid.TileGrid.prototype.getTileCoordChildTileRange = function(tileCoord, opt_tileRange, opt_extent) { + if (tileCoord[0] < this.maxZoom) { + var tileCoordExtent = this.getTileCoordExtent(tileCoord, opt_extent); + return this.getTileRangeForExtentAndZ( + tileCoordExtent, tileCoord[0] + 1, opt_tileRange); + } else { + return null; + } +}; + + +/** + * @param {number} z Z. + * @param {ol.TileRange} tileRange Tile range. + * @param {ol.Extent=} opt_extent Temporary ol.Extent object. + * @return {ol.Extent} Extent. + */ +ol.tilegrid.TileGrid.prototype.getTileRangeExtent = function(z, tileRange, opt_extent) { + var origin = this.getOrigin(z); + var resolution = this.getResolution(z); + var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_); + var minX = origin[0] + tileRange.minX * tileSize[0] * resolution; + var maxX = origin[0] + (tileRange.maxX + 1) * tileSize[0] * resolution; + var minY = origin[1] + tileRange.minY * tileSize[1] * resolution; + var maxY = origin[1] + (tileRange.maxY + 1) * tileSize[1] * resolution; + return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent); +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {ol.TileRange=} opt_tileRange Temporary tile range object. + * @return {ol.TileRange} Tile range. + */ +ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function(extent, resolution, opt_tileRange) { + var tileCoord = ol.tilegrid.TileGrid.tmpTileCoord_; + this.getTileCoordForXYAndResolution_( + extent[0], extent[1], resolution, false, tileCoord); + var minX = tileCoord[1]; + var minY = tileCoord[2]; + this.getTileCoordForXYAndResolution_( + extent[2], extent[3], resolution, true, tileCoord); + return ol.TileRange.createOrUpdate( + minX, tileCoord[1], minY, tileCoord[2], opt_tileRange); +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} z Z. + * @param {ol.TileRange=} opt_tileRange Temporary tile range object. + * @return {ol.TileRange} Tile range. + */ +ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndZ = function(extent, z, opt_tileRange) { + var resolution = this.getResolution(z); + return this.getTileRangeForExtentAndResolution( + extent, resolution, opt_tileRange); +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @return {ol.Coordinate} Tile center. + */ +ol.tilegrid.TileGrid.prototype.getTileCoordCenter = function(tileCoord) { + var origin = this.getOrigin(tileCoord[0]); + var resolution = this.getResolution(tileCoord[0]); + var tileSize = ol.size.toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); + return [ + origin[0] + (tileCoord[1] + 0.5) * tileSize[0] * resolution, + origin[1] + (tileCoord[2] + 0.5) * tileSize[1] * resolution + ]; +}; + + +/** + * Get the extent of a tile coordinate. + * + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.Extent=} opt_extent Temporary extent object. + * @return {ol.Extent} Extent. + * @api + */ +ol.tilegrid.TileGrid.prototype.getTileCoordExtent = function(tileCoord, opt_extent) { + var origin = this.getOrigin(tileCoord[0]); + var resolution = this.getResolution(tileCoord[0]); + var tileSize = ol.size.toSize(this.getTileSize(tileCoord[0]), this.tmpSize_); + var minX = origin[0] + tileCoord[1] * tileSize[0] * resolution; + var minY = origin[1] + tileCoord[2] * tileSize[1] * resolution; + var maxX = minX + tileSize[0] * resolution; + var maxY = minY + tileSize[1] * resolution; + return ol.extent.createOrUpdate(minX, minY, maxX, maxY, opt_extent); +}; + + +/** + * Get the tile coordinate for the given map coordinate and resolution. This + * method considers that coordinates that intersect tile boundaries should be + * assigned the higher tile coordinate. + * + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object. + * @return {ol.TileCoord} Tile coordinate. + * @api + */ +ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution = function(coordinate, resolution, opt_tileCoord) { + return this.getTileCoordForXYAndResolution_( + coordinate[0], coordinate[1], resolution, false, opt_tileCoord); +}; + + +/** + * @param {number} x X. + * @param {number} y Y. + * @param {number} resolution Resolution. + * @param {boolean} reverseIntersectionPolicy Instead of letting edge + * intersections go to the higher tile coordinate, let edge intersections + * go to the lower tile coordinate. + * @param {ol.TileCoord=} opt_tileCoord Temporary ol.TileCoord object. + * @return {ol.TileCoord} Tile coordinate. + * @private + */ +ol.tilegrid.TileGrid.prototype.getTileCoordForXYAndResolution_ = function( + x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) { + var z = this.getZForResolution(resolution); + var scale = resolution / this.getResolution(z); + var origin = this.getOrigin(z); + var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_); + + var adjustX = reverseIntersectionPolicy ? 0.5 : 0; + var adjustY = reverseIntersectionPolicy ? 0 : 0.5; + var xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX); + var yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY); + var tileCoordX = scale * xFromOrigin / tileSize[0]; + var tileCoordY = scale * yFromOrigin / tileSize[1]; + + if (reverseIntersectionPolicy) { + tileCoordX = Math.ceil(tileCoordX) - 1; + tileCoordY = Math.ceil(tileCoordY) - 1; + } else { + tileCoordX = Math.floor(tileCoordX); + tileCoordY = Math.floor(tileCoordY); + } + + return ol.tilecoord.createOrUpdate(z, tileCoordX, tileCoordY, opt_tileCoord); +}; + + +/** + * Get a tile coordinate given a map coordinate and zoom level. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} z Zoom level. + * @param {ol.TileCoord=} opt_tileCoord Destination ol.TileCoord object. + * @return {ol.TileCoord} Tile coordinate. + * @api + */ +ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ = function(coordinate, z, opt_tileCoord) { + var resolution = this.getResolution(z); + return this.getTileCoordForXYAndResolution_( + coordinate[0], coordinate[1], resolution, false, opt_tileCoord); +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @return {number} Tile resolution. + */ +ol.tilegrid.TileGrid.prototype.getTileCoordResolution = function(tileCoord) { + goog.asserts.assert( + this.minZoom <= tileCoord[0] && tileCoord[0] <= this.maxZoom, + 'z of given tilecoord is not in allowed range (%s <= %s <= %s', + this.minZoom, tileCoord[0], this.maxZoom); + return this.resolutions_[tileCoord[0]]; +}; + + +/** + * Get the tile size for a zoom level. The type of the return value matches the + * `tileSize` or `tileSizes` that the tile grid was configured with. To always + * get an `ol.Size`, run the result through `ol.size.toSize()`. + * @param {number} z Z. + * @return {number|ol.Size} Tile size. + * @api stable + */ +ol.tilegrid.TileGrid.prototype.getTileSize = function(z) { + if (this.tileSize_) { + return this.tileSize_; + } else { + goog.asserts.assert(this.tileSizes_, + 'tileSizes cannot be null if tileSize is null'); + goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom, + 'z is not in allowed range (%s <= %s <= %s', + this.minZoom, z, this.maxZoom); + return this.tileSizes_[z]; + } +}; + + +/** + * @param {number} z Zoom level. + * @return {ol.TileRange} Extent tile range for the specified zoom level. + */ +ol.tilegrid.TileGrid.prototype.getFullTileRange = function(z) { + if (!this.fullTileRanges_) { + return null; + } else { + goog.asserts.assert(this.minZoom <= z && z <= this.maxZoom, + 'z is not in allowed range (%s <= %s <= %s', + this.minZoom, z, this.maxZoom); + return this.fullTileRanges_[z]; + } +}; + + +/** + * @param {number} resolution Resolution. + * @param {number=} opt_direction If 0, the nearest resolution will be used. + * If 1, the nearest lower resolution will be used. If -1, the nearest + * higher resolution will be used. Default is 0. + * @return {number} Z. + * @api + */ +ol.tilegrid.TileGrid.prototype.getZForResolution = function( + resolution, opt_direction) { + var z = ol.array.linearFindNearest(this.resolutions_, resolution, + opt_direction || 0); + return ol.math.clamp(z, this.minZoom, this.maxZoom); +}; + + +/** + * @param {!ol.Extent} extent Extent for this tile grid. + * @private + */ +ol.tilegrid.TileGrid.prototype.calculateTileRanges_ = function(extent) { + var length = this.resolutions_.length; + var fullTileRanges = new Array(length); + for (var z = this.minZoom; z < length; ++z) { + fullTileRanges[z] = this.getTileRangeForExtentAndZ(extent, z); + } + this.fullTileRanges_ = fullTileRanges; +}; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @return {ol.tilegrid.TileGrid} Default tile grid for the passed projection. + */ +ol.tilegrid.getForProjection = function(projection) { + var tileGrid = projection.getDefaultTileGrid(); + if (!tileGrid) { + tileGrid = ol.tilegrid.createForProjection(projection); + projection.setDefaultTileGrid(tileGrid); + } + return tileGrid; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number=} opt_maxZoom Maximum zoom level (default is + * ol.DEFAULT_MAX_ZOOM). + * @param {number|ol.Size=} opt_tileSize Tile size (default uses + * ol.DEFAULT_TILE_SIZE). + * @param {ol.extent.Corner=} opt_corner Extent corner (default is + * ol.extent.Corner.TOP_LEFT). + * @return {ol.tilegrid.TileGrid} TileGrid instance. + */ +ol.tilegrid.createForExtent = function(extent, opt_maxZoom, opt_tileSize, opt_corner) { + var corner = opt_corner !== undefined ? + opt_corner : ol.extent.Corner.TOP_LEFT; + + var resolutions = ol.tilegrid.resolutionsFromExtent( + extent, opt_maxZoom, opt_tileSize); + + return new ol.tilegrid.TileGrid({ + extent: extent, + origin: ol.extent.getCorner(extent, corner), + resolutions: resolutions, + tileSize: opt_tileSize + }); +}; + + +/** + * Creates a tile grid with a standard XYZ tiling scheme. + * @param {olx.tilegrid.XYZOptions=} opt_options Tile grid options. + * @return {ol.tilegrid.TileGrid} Tile grid instance. + * @api + */ +ol.tilegrid.createXYZ = function(opt_options) { + var options = /** @type {olx.tilegrid.TileGridOptions} */ ({}); + ol.object.assign(options, opt_options !== undefined ? + opt_options : /** @type {olx.tilegrid.XYZOptions} */ ({})); + if (options.extent === undefined) { + options.extent = ol.proj.get('EPSG:3857').getExtent(); + } + options.resolutions = ol.tilegrid.resolutionsFromExtent( + options.extent, options.maxZoom, options.tileSize); + delete options.maxZoom; + + return new ol.tilegrid.TileGrid(options); +}; + + +/** + * Create a resolutions array from an extent. A zoom factor of 2 is assumed. + * @param {ol.Extent} extent Extent. + * @param {number=} opt_maxZoom Maximum zoom level (default is + * ol.DEFAULT_MAX_ZOOM). + * @param {number|ol.Size=} opt_tileSize Tile size (default uses + * ol.DEFAULT_TILE_SIZE). + * @return {!Array.<number>} Resolutions array. + */ +ol.tilegrid.resolutionsFromExtent = function(extent, opt_maxZoom, opt_tileSize) { + var maxZoom = opt_maxZoom !== undefined ? + opt_maxZoom : ol.DEFAULT_MAX_ZOOM; + + var height = ol.extent.getHeight(extent); + var width = ol.extent.getWidth(extent); + + var tileSize = ol.size.toSize(opt_tileSize !== undefined ? + opt_tileSize : ol.DEFAULT_TILE_SIZE); + var maxResolution = Math.max( + width / tileSize[0], height / tileSize[1]); + + var length = maxZoom + 1; + var resolutions = new Array(length); + for (var z = 0; z < length; ++z) { + resolutions[z] = maxResolution / Math.pow(2, z); + } + return resolutions; +}; + + +/** + * @param {ol.ProjectionLike} projection Projection. + * @param {number=} opt_maxZoom Maximum zoom level (default is + * ol.DEFAULT_MAX_ZOOM). + * @param {ol.Size=} opt_tileSize Tile size (default uses ol.DEFAULT_TILE_SIZE). + * @param {ol.extent.Corner=} opt_corner Extent corner (default is + * ol.extent.Corner.BOTTOM_LEFT). + * @return {ol.tilegrid.TileGrid} TileGrid instance. + */ +ol.tilegrid.createForProjection = function(projection, opt_maxZoom, opt_tileSize, opt_corner) { + var extent = ol.tilegrid.extentFromProjection(projection); + return ol.tilegrid.createForExtent( + extent, opt_maxZoom, opt_tileSize, opt_corner); +}; + + +/** + * Generate a tile grid extent from a projection. If the projection has an + * extent, it is used. If not, a global extent is assumed. + * @param {ol.ProjectionLike} projection Projection. + * @return {ol.Extent} Extent. + */ +ol.tilegrid.extentFromProjection = function(projection) { + projection = ol.proj.get(projection); + var extent = projection.getExtent(); + if (!extent) { + var half = 180 * ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES] / + projection.getMetersPerUnit(); + extent = ol.extent.createOrUpdate(-half, -half, half, half); + } + return extent; +}; + +goog.provide('ol.source.Tile'); +goog.provide('ol.source.TileEvent'); + +goog.require('goog.asserts'); +goog.require('ol.events.Event'); +goog.require('ol'); +goog.require('ol.TileCache'); +goog.require('ol.TileRange'); +goog.require('ol.TileState'); +goog.require('ol.proj'); +goog.require('ol.size'); +goog.require('ol.source.Source'); +goog.require('ol.tilecoord'); +goog.require('ol.tilegrid.TileGrid'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for sources providing images divided into a tile grid. + * + * @constructor + * @extends {ol.source.Source} + * @param {ol.SourceTileOptions} options Tile source options. + * @api + */ +ol.source.Tile = function(options) { + + ol.source.Source.call(this, { + attributions: options.attributions, + extent: options.extent, + logo: options.logo, + projection: options.projection, + state: options.state, + wrapX: options.wrapX + }); + + /** + * @private + * @type {boolean} + */ + this.opaque_ = options.opaque !== undefined ? options.opaque : false; + + /** + * @private + * @type {number} + */ + this.tilePixelRatio_ = options.tilePixelRatio !== undefined ? + options.tilePixelRatio : 1; + + /** + * @protected + * @type {ol.tilegrid.TileGrid} + */ + this.tileGrid = options.tileGrid !== undefined ? options.tileGrid : null; + + /** + * @protected + * @type {ol.TileCache} + */ + this.tileCache = new ol.TileCache(options.cacheSize); + + /** + * @protected + * @type {ol.Size} + */ + this.tmpSize = [0, 0]; + + /** + * @private + * @type {string} + */ + this.key_ = ''; + +}; +ol.inherits(ol.source.Tile, ol.source.Source); + + +/** + * @return {boolean} Can expire cache. + */ +ol.source.Tile.prototype.canExpireCache = function() { + return this.tileCache.canExpireCache(); +}; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @param {Object.<string, ol.TileRange>} usedTiles Used tiles. + */ +ol.source.Tile.prototype.expireCache = function(projection, usedTiles) { + var tileCache = this.getTileCacheForProjection(projection); + if (tileCache) { + tileCache.expireCache(usedTiles); + } +}; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @param {number} z Zoom level. + * @param {ol.TileRange} tileRange Tile range. + * @param {function(ol.Tile):(boolean|undefined)} callback Called with each + * loaded tile. If the callback returns `false`, the tile will not be + * considered loaded. + * @return {boolean} The tile range is fully covered with loaded tiles. + */ +ol.source.Tile.prototype.forEachLoadedTile = function(projection, z, tileRange, callback) { + var tileCache = this.getTileCacheForProjection(projection); + if (!tileCache) { + return false; + } + + var covered = true; + var tile, tileCoordKey, loaded; + for (var x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (var y = tileRange.minY; y <= tileRange.maxY; ++y) { + tileCoordKey = this.getKeyZXY(z, x, y); + loaded = false; + if (tileCache.containsKey(tileCoordKey)) { + tile = /** @type {!ol.Tile} */ (tileCache.get(tileCoordKey)); + loaded = tile.getState() === ol.TileState.LOADED; + if (loaded) { + loaded = (callback(tile) !== false); + } + } + if (!loaded) { + covered = false; + } + } + } + return covered; +}; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @return {number} Gutter. + */ +ol.source.Tile.prototype.getGutter = function(projection) { + return 0; +}; + + +/** + * Return the key to be used for all tiles in the source. + * @return {string} The key for all tiles. + * @protected + */ +ol.source.Tile.prototype.getKey = function() { + return this.key_; +}; + + +/** + * Set the value to be used as the key for all tiles in the source. + * @param {string} key The key for tiles. + * @protected + */ +ol.source.Tile.prototype.setKey = function(key) { + if (this.key_ !== key) { + this.key_ = key; + this.changed(); + } +}; + + +/** + * @param {number} z Z. + * @param {number} x X. + * @param {number} y Y. + * @return {string} Key. + * @protected + */ +ol.source.Tile.prototype.getKeyZXY = ol.tilecoord.getKeyZXY; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @return {boolean} Opaque. + */ +ol.source.Tile.prototype.getOpaque = function(projection) { + return this.opaque_; +}; + + +/** + * @inheritDoc + */ +ol.source.Tile.prototype.getResolutions = function() { + return this.tileGrid.getResolutions(); +}; + + +/** + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {!ol.Tile} Tile. + */ +ol.source.Tile.prototype.getTile = goog.abstractMethod; + + +/** + * Return the tile grid of the tile source. + * @return {ol.tilegrid.TileGrid} Tile grid. + * @api stable + */ +ol.source.Tile.prototype.getTileGrid = function() { + return this.tileGrid; +}; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @return {ol.tilegrid.TileGrid} Tile grid. + */ +ol.source.Tile.prototype.getTileGridForProjection = function(projection) { + if (!this.tileGrid) { + return ol.tilegrid.getForProjection(projection); + } else { + return this.tileGrid; + } +}; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @return {ol.TileCache} Tile cache. + * @protected + */ +ol.source.Tile.prototype.getTileCacheForProjection = function(projection) { + var thisProj = this.getProjection(); + if (thisProj && !ol.proj.equivalent(thisProj, projection)) { + return null; + } else { + return this.tileCache; + } +}; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {number} Tile pixel ratio. + */ +ol.source.Tile.prototype.getTilePixelRatio = function(pixelRatio) { + return this.tilePixelRatio_; +}; + + +/** + * @param {number} z Z. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {ol.Size} Tile size. + */ +ol.source.Tile.prototype.getTilePixelSize = function(z, pixelRatio, projection) { + var tileGrid = this.getTileGridForProjection(projection); + var tilePixelRatio = this.getTilePixelRatio(pixelRatio); + var tileSize = ol.size.toSize(tileGrid.getTileSize(z), this.tmpSize); + if (tilePixelRatio == 1) { + return tileSize; + } else { + return ol.size.scale(tileSize, tilePixelRatio, this.tmpSize); + } +}; + + +/** + * Returns a tile coordinate wrapped around the x-axis. When the tile coordinate + * is outside the resolution and extent range of the tile grid, `null` will be + * returned. + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.proj.Projection=} opt_projection Projection. + * @return {ol.TileCoord} Tile coordinate to be passed to the tileUrlFunction or + * null if no tile URL should be created for the passed `tileCoord`. + */ +ol.source.Tile.prototype.getTileCoordForTileUrlFunction = function(tileCoord, opt_projection) { + var projection = opt_projection !== undefined ? + opt_projection : this.getProjection(); + var tileGrid = this.getTileGridForProjection(projection); + goog.asserts.assert(tileGrid, 'tile grid needed'); + if (this.getWrapX() && projection.isGlobal()) { + tileCoord = ol.tilecoord.wrapX(tileCoord, tileGrid, projection); + } + return ol.tilecoord.withinExtentAndZ(tileCoord, tileGrid) ? tileCoord : null; +}; + + +/** + * @inheritDoc + */ +ol.source.Tile.prototype.refresh = function() { + this.tileCache.clear(); + this.changed(); +}; + + +/** + * Marks a tile coord as being used, without triggering a load. + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {ol.proj.Projection} projection Projection. + */ +ol.source.Tile.prototype.useTile = ol.nullFunction; + + +/** + * @classdesc + * Events emitted by {@link ol.source.Tile} instances are instances of this + * type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.source.TileEvent} + * @param {string} type Type. + * @param {ol.Tile} tile The tile. + */ +ol.source.TileEvent = function(type, tile) { + + ol.events.Event.call(this, type); + + /** + * The tile related to the event. + * @type {ol.Tile} + * @api + */ + this.tile = tile; + +}; +ol.inherits(ol.source.TileEvent, ol.events.Event); + + +/** + * @enum {string} + */ +ol.source.TileEventType = { + + /** + * Triggered when a tile starts loading. + * @event ol.source.TileEvent#tileloadstart + * @api stable + */ + TILELOADSTART: 'tileloadstart', + + /** + * Triggered when a tile finishes loading. + * @event ol.source.TileEvent#tileloadend + * @api stable + */ + TILELOADEND: 'tileloadend', + + /** + * Triggered if tile loading results in an error. + * @event ol.source.TileEvent#tileloaderror + * @api stable + */ + TILELOADERROR: 'tileloaderror' + +}; + +// FIXME handle date line wrap + +goog.provide('ol.control.Attribution'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.dom'); +goog.require('ol.Attribution'); +goog.require('ol.control.Control'); +goog.require('ol.css'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.object'); +goog.require('ol.source.Tile'); + + +/** + * @classdesc + * Control to show all the attributions associated with the layer sources + * in the map. This control is one of the default controls included in maps. + * By default it will show in the bottom right portion of the map, but this can + * be changed by using a css selector for `.ol-attribution`. + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.AttributionOptions=} opt_options Attribution options. + * @api stable + */ +ol.control.Attribution = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {Element} + */ + this.ulElement_ = document.createElement('UL'); + + /** + * @private + * @type {Element} + */ + this.logoLi_ = document.createElement('LI'); + + this.ulElement_.appendChild(this.logoLi_); + this.logoLi_.style.display = 'none'; + + /** + * @private + * @type {boolean} + */ + this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; + + /** + * @private + * @type {boolean} + */ + this.collapsible_ = options.collapsible !== undefined ? + options.collapsible : true; + + if (!this.collapsible_) { + this.collapsed_ = false; + } + + var className = options.className !== undefined ? options.className : 'ol-attribution'; + + var tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Attributions'; + + var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00BB'; + + if (typeof collapseLabel === 'string') { + /** + * @private + * @type {Node} + */ + this.collapseLabel_ = document.createElement('span'); + this.collapseLabel_.textContent = collapseLabel; + } else { + this.collapseLabel_ = collapseLabel; + } + + var label = options.label !== undefined ? options.label : 'i'; + + if (typeof label === 'string') { + /** + * @private + * @type {Node} + */ + this.label_ = document.createElement('span'); + this.label_.textContent = label; + } else { + this.label_ = label; + } + + + var activeLabel = (this.collapsible_ && !this.collapsed_) ? + this.collapseLabel_ : this.label_; + var button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild(activeLabel); + + ol.events.listen(button, ol.events.EventType.CLICK, this.handleClick_, this); + + var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + + ol.css.CLASS_CONTROL + + (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') + + (this.collapsible_ ? '' : ' ol-uncollapsible'); + var element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(this.ulElement_); + element.appendChild(button); + + var render = options.render ? options.render : ol.control.Attribution.render; + + ol.control.Control.call(this, { + element: element, + render: render, + target: options.target + }); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + + /** + * @private + * @type {Object.<string, Element>} + */ + this.attributionElements_ = {}; + + /** + * @private + * @type {Object.<string, boolean>} + */ + this.attributionElementRenderedVisible_ = {}; + + /** + * @private + * @type {Object.<string, Element>} + */ + this.logoElements_ = {}; + +}; +ol.inherits(ol.control.Attribution, ol.control.Control); + + +/** + * @param {?olx.FrameState} frameState Frame state. + * @return {Array.<Object.<string, ol.Attribution>>} Attributions. + */ +ol.control.Attribution.prototype.getSourceAttributions = function(frameState) { + var i, ii, j, jj, tileRanges, source, sourceAttribution, + sourceAttributionKey, sourceAttributions, sourceKey; + var intersectsTileRange; + var layerStatesArray = frameState.layerStatesArray; + /** @type {Object.<string, ol.Attribution>} */ + var attributions = ol.object.assign({}, frameState.attributions); + /** @type {Object.<string, ol.Attribution>} */ + var hiddenAttributions = {}; + var projection = frameState.viewState.projection; + goog.asserts.assert(projection, 'projection of viewState required'); + for (i = 0, ii = layerStatesArray.length; i < ii; i++) { + source = layerStatesArray[i].layer.getSource(); + if (!source) { + continue; + } + sourceKey = goog.getUid(source).toString(); + sourceAttributions = source.getAttributions(); + if (!sourceAttributions) { + continue; + } + for (j = 0, jj = sourceAttributions.length; j < jj; j++) { + sourceAttribution = sourceAttributions[j]; + sourceAttributionKey = goog.getUid(sourceAttribution).toString(); + if (sourceAttributionKey in attributions) { + continue; + } + tileRanges = frameState.usedTiles[sourceKey]; + if (tileRanges) { + goog.asserts.assertInstanceof(source, ol.source.Tile, + 'source should be an ol.source.Tile'); + var tileGrid = source.getTileGridForProjection(projection); + goog.asserts.assert(tileGrid, 'tileGrid required for projection'); + intersectsTileRange = sourceAttribution.intersectsAnyTileRange( + tileRanges, tileGrid, projection); + } else { + intersectsTileRange = false; + } + if (intersectsTileRange) { + if (sourceAttributionKey in hiddenAttributions) { + delete hiddenAttributions[sourceAttributionKey]; + } + attributions[sourceAttributionKey] = sourceAttribution; + } else { + hiddenAttributions[sourceAttributionKey] = sourceAttribution; + } + } + } + return [attributions, hiddenAttributions]; +}; + + +/** + * Update the attribution element. + * @param {ol.MapEvent} mapEvent Map event. + * @this {ol.control.Attribution} + * @api + */ +ol.control.Attribution.render = function(mapEvent) { + this.updateElement_(mapEvent.frameState); +}; + + +/** + * @private + * @param {?olx.FrameState} frameState Frame state. + */ +ol.control.Attribution.prototype.updateElement_ = function(frameState) { + + if (!frameState) { + if (this.renderedVisible_) { + this.element.style.display = 'none'; + this.renderedVisible_ = false; + } + return; + } + + var attributions = this.getSourceAttributions(frameState); + /** @type {Object.<string, ol.Attribution>} */ + var visibleAttributions = attributions[0]; + /** @type {Object.<string, ol.Attribution>} */ + var hiddenAttributions = attributions[1]; + + var attributionElement, attributionKey; + for (attributionKey in this.attributionElements_) { + if (attributionKey in visibleAttributions) { + if (!this.attributionElementRenderedVisible_[attributionKey]) { + this.attributionElements_[attributionKey].style.display = ''; + this.attributionElementRenderedVisible_[attributionKey] = true; + } + delete visibleAttributions[attributionKey]; + } else if (attributionKey in hiddenAttributions) { + if (this.attributionElementRenderedVisible_[attributionKey]) { + this.attributionElements_[attributionKey].style.display = 'none'; + delete this.attributionElementRenderedVisible_[attributionKey]; + } + delete hiddenAttributions[attributionKey]; + } else { + ol.dom.removeNode(this.attributionElements_[attributionKey]); + delete this.attributionElements_[attributionKey]; + delete this.attributionElementRenderedVisible_[attributionKey]; + } + } + for (attributionKey in visibleAttributions) { + attributionElement = document.createElement('LI'); + attributionElement.innerHTML = + visibleAttributions[attributionKey].getHTML(); + this.ulElement_.appendChild(attributionElement); + this.attributionElements_[attributionKey] = attributionElement; + this.attributionElementRenderedVisible_[attributionKey] = true; + } + for (attributionKey in hiddenAttributions) { + attributionElement = document.createElement('LI'); + attributionElement.innerHTML = + hiddenAttributions[attributionKey].getHTML(); + attributionElement.style.display = 'none'; + this.ulElement_.appendChild(attributionElement); + this.attributionElements_[attributionKey] = attributionElement; + } + + var renderVisible = + !ol.object.isEmpty(this.attributionElementRenderedVisible_) || + !ol.object.isEmpty(frameState.logos); + if (this.renderedVisible_ != renderVisible) { + this.element.style.display = renderVisible ? '' : 'none'; + this.renderedVisible_ = renderVisible; + } + if (renderVisible && + ol.object.isEmpty(this.attributionElementRenderedVisible_)) { + this.element.classList.add('ol-logo-only'); + } else { + this.element.classList.remove('ol-logo-only'); + } + + this.insertLogos_(frameState); + +}; + + +/** + * @param {?olx.FrameState} frameState Frame state. + * @private + */ +ol.control.Attribution.prototype.insertLogos_ = function(frameState) { + + var logo; + var logos = frameState.logos; + var logoElements = this.logoElements_; + + for (logo in logoElements) { + if (!(logo in logos)) { + ol.dom.removeNode(logoElements[logo]); + delete logoElements[logo]; + } + } + + var image, logoElement, logoKey; + for (logoKey in logos) { + var logoValue = logos[logoKey]; + if (logoValue instanceof HTMLElement) { + this.logoLi_.appendChild(logoValue); + logoElements[logoKey] = logoValue; + } + if (!(logoKey in logoElements)) { + image = new Image(); + image.src = logoKey; + if (logoValue === '') { + logoElement = image; + } else { + logoElement = document.createElement('a'); + logoElement.href = logoValue; + logoElement.appendChild(image); + } + this.logoLi_.appendChild(logoElement); + logoElements[logoKey] = logoElement; + } + } + + this.logoLi_.style.display = !ol.object.isEmpty(logos) ? '' : 'none'; + +}; + + +/** + * @param {Event} event The event to handle + * @private + */ +ol.control.Attribution.prototype.handleClick_ = function(event) { + event.preventDefault(); + this.handleToggle_(); +}; + + +/** + * @private + */ +ol.control.Attribution.prototype.handleToggle_ = function() { + this.element.classList.toggle('ol-collapsed'); + if (this.collapsed_) { + ol.dom.replaceNode(this.collapseLabel_, this.label_); + } else { + ol.dom.replaceNode(this.label_, this.collapseLabel_); + } + this.collapsed_ = !this.collapsed_; +}; + + +/** + * Return `true` if the attribution is collapsible, `false` otherwise. + * @return {boolean} True if the widget is collapsible. + * @api stable + */ +ol.control.Attribution.prototype.getCollapsible = function() { + return this.collapsible_; +}; + + +/** + * Set whether the attribution should be collapsible. + * @param {boolean} collapsible True if the widget is collapsible. + * @api stable + */ +ol.control.Attribution.prototype.setCollapsible = function(collapsible) { + if (this.collapsible_ === collapsible) { + return; + } + this.collapsible_ = collapsible; + this.element.classList.toggle('ol-uncollapsible'); + if (!collapsible && this.collapsed_) { + this.handleToggle_(); + } +}; + + +/** + * Collapse or expand the attribution according to the passed parameter. Will + * not do anything if the attribution isn't collapsible or if the current + * collapsed state is already the one requested. + * @param {boolean} collapsed True if the widget is collapsed. + * @api stable + */ +ol.control.Attribution.prototype.setCollapsed = function(collapsed) { + if (!this.collapsible_ || this.collapsed_ === collapsed) { + return; + } + this.handleToggle_(); +}; + + +/** + * Return `true` when the attribution is currently collapsed or `false` + * otherwise. + * @return {boolean} True if the widget is collapsed. + * @api stable + */ +ol.control.Attribution.prototype.getCollapsed = function() { + return this.collapsed_; +}; + +goog.provide('ol.control.Rotate'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol'); +goog.require('ol.animation'); +goog.require('ol.control.Control'); +goog.require('ol.css'); +goog.require('ol.easing'); + + +/** + * @classdesc + * A button control to reset rotation to 0. + * To style this control use css selector `.ol-rotate`. A `.ol-hidden` css + * selector is added to the button when the rotation is 0. + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.RotateOptions=} opt_options Rotate options. + * @api stable + */ +ol.control.Rotate = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + var className = options.className !== undefined ? options.className : 'ol-rotate'; + + var label = options.label !== undefined ? options.label : '\u21E7'; + + /** + * @type {Element} + * @private + */ + this.label_ = null; + + if (typeof label === 'string') { + this.label_ = document.createElement('span'); + this.label_.className = 'ol-compass'; + this.label_.textContent = label; + } else { + this.label_ = label; + this.label_.classList.add('ol-compass'); + } + + var tipLabel = options.tipLabel ? options.tipLabel : 'Reset rotation'; + + var button = document.createElement('button'); + button.className = className + '-reset'; + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild(this.label_); + + ol.events.listen(button, ol.events.EventType.CLICK, + ol.control.Rotate.prototype.handleClick_, this); + + var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + + ol.css.CLASS_CONTROL; + var element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(button); + + var render = options.render ? options.render : ol.control.Rotate.render; + + this.callResetNorth_ = options.resetNorth ? options.resetNorth : undefined; + + ol.control.Control.call(this, { + element: element, + render: render, + target: options.target + }); + + /** + * @type {number} + * @private + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + /** + * @type {boolean} + * @private + */ + this.autoHide_ = options.autoHide !== undefined ? options.autoHide : true; + + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = undefined; + + if (this.autoHide_) { + this.element.classList.add(ol.css.CLASS_HIDDEN); + } + +}; +ol.inherits(ol.control.Rotate, ol.control.Control); + + +/** + * @param {Event} event The event to handle + * @private + */ +ol.control.Rotate.prototype.handleClick_ = function(event) { + event.preventDefault(); + if (this.callResetNorth_ !== undefined) { + this.callResetNorth_(); + } else { + this.resetNorth_(); + } +}; + + +/** + * @private + */ +ol.control.Rotate.prototype.resetNorth_ = function() { + var map = this.getMap(); + var view = map.getView(); + if (!view) { + // the map does not have a view, so we can't act + // upon it + return; + } + var currentRotation = view.getRotation(); + if (currentRotation !== undefined) { + if (this.duration_ > 0) { + currentRotation = currentRotation % (2 * Math.PI); + if (currentRotation < -Math.PI) { + currentRotation += 2 * Math.PI; + } + if (currentRotation > Math.PI) { + currentRotation -= 2 * Math.PI; + } + map.beforeRender(ol.animation.rotate({ + rotation: currentRotation, + duration: this.duration_, + easing: ol.easing.easeOut + })); + } + view.setRotation(0); + } +}; + + +/** + * Update the rotate control element. + * @param {ol.MapEvent} mapEvent Map event. + * @this {ol.control.Rotate} + * @api + */ +ol.control.Rotate.render = function(mapEvent) { + var frameState = mapEvent.frameState; + if (!frameState) { + return; + } + var rotation = frameState.viewState.rotation; + if (rotation != this.rotation_) { + var transform = 'rotate(' + rotation + 'rad)'; + if (this.autoHide_) { + var contains = this.element.classList.contains(ol.css.CLASS_HIDDEN); + if (!contains && rotation === 0) { + this.element.classList.add(ol.css.CLASS_HIDDEN); + } else if (contains && rotation !== 0) { + this.element.classList.remove(ol.css.CLASS_HIDDEN); + } + } + this.label_.style.msTransform = transform; + this.label_.style.webkitTransform = transform; + this.label_.style.transform = transform; + } + this.rotation_ = rotation; +}; + +goog.provide('ol.control.Zoom'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.animation'); +goog.require('ol.control.Control'); +goog.require('ol.css'); +goog.require('ol.easing'); + + +/** + * @classdesc + * A control with 2 buttons, one for zoom in and one for zoom out. + * This control is one of the default controls of a map. To style this control + * use css selectors `.ol-zoom-in` and `.ol-zoom-out`. + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.ZoomOptions=} opt_options Zoom options. + * @api stable + */ +ol.control.Zoom = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + var className = options.className !== undefined ? options.className : 'ol-zoom'; + + var delta = options.delta !== undefined ? options.delta : 1; + + var zoomInLabel = options.zoomInLabel !== undefined ? options.zoomInLabel : '+'; + var zoomOutLabel = options.zoomOutLabel !== undefined ? options.zoomOutLabel : '\u2212'; + + var zoomInTipLabel = options.zoomInTipLabel !== undefined ? + options.zoomInTipLabel : 'Zoom in'; + var zoomOutTipLabel = options.zoomOutTipLabel !== undefined ? + options.zoomOutTipLabel : 'Zoom out'; + + var inElement = document.createElement('button'); + inElement.className = className + '-in'; + inElement.setAttribute('type', 'button'); + inElement.title = zoomInTipLabel; + inElement.appendChild( + typeof zoomInLabel === 'string' ? document.createTextNode(zoomInLabel) : zoomInLabel + ); + + ol.events.listen(inElement, ol.events.EventType.CLICK, + ol.control.Zoom.prototype.handleClick_.bind(this, delta)); + + var outElement = document.createElement('button'); + outElement.className = className + '-out'; + outElement.setAttribute('type', 'button'); + outElement.title = zoomOutTipLabel; + outElement.appendChild( + typeof zoomOutLabel === 'string' ? document.createTextNode(zoomOutLabel) : zoomOutLabel + ); + + ol.events.listen(outElement, ol.events.EventType.CLICK, + ol.control.Zoom.prototype.handleClick_.bind(this, -delta)); + + var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + + ol.css.CLASS_CONTROL; + var element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(inElement); + element.appendChild(outElement); + + ol.control.Control.call(this, { + element: element, + target: options.target + }); + + /** + * @type {number} + * @private + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + +}; +ol.inherits(ol.control.Zoom, ol.control.Control); + + +/** + * @param {number} delta Zoom delta. + * @param {Event} event The event to handle + * @private + */ +ol.control.Zoom.prototype.handleClick_ = function(delta, event) { + event.preventDefault(); + this.zoomByDelta_(delta); +}; + + +/** + * @param {number} delta Zoom delta. + * @private + */ +ol.control.Zoom.prototype.zoomByDelta_ = function(delta) { + var map = this.getMap(); + var view = map.getView(); + if (!view) { + // the map does not have a view, so we can't act + // upon it + return; + } + var currentResolution = view.getResolution(); + if (currentResolution) { + if (this.duration_ > 0) { + map.beforeRender(ol.animation.zoom({ + resolution: currentResolution, + duration: this.duration_, + easing: ol.easing.easeOut + })); + } + var newResolution = view.constrainResolution(currentResolution, delta); + view.setResolution(newResolution); + } +}; + +goog.provide('ol.control'); + +goog.require('ol'); +goog.require('ol.Collection'); +goog.require('ol.control.Attribution'); +goog.require('ol.control.Rotate'); +goog.require('ol.control.Zoom'); + + +/** + * Set of controls included in maps by default. Unless configured otherwise, + * this returns a collection containing an instance of each of the following + * controls: + * * {@link ol.control.Zoom} + * * {@link ol.control.Rotate} + * * {@link ol.control.Attribution} + * + * @param {olx.control.DefaultsOptions=} opt_options Defaults options. + * @return {ol.Collection.<ol.control.Control>} Controls. + * @api stable + */ +ol.control.defaults = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + var controls = new ol.Collection(); + + var zoomControl = options.zoom !== undefined ? options.zoom : true; + if (zoomControl) { + controls.push(new ol.control.Zoom(options.zoomOptions)); + } + + var rotateControl = options.rotate !== undefined ? options.rotate : true; + if (rotateControl) { + controls.push(new ol.control.Rotate(options.rotateOptions)); + } + + var attributionControl = options.attribution !== undefined ? + options.attribution : true; + if (attributionControl) { + controls.push(new ol.control.Attribution(options.attributionOptions)); + } + + return controls; + +}; + +goog.provide('ol.control.FullScreen'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol'); +goog.require('ol.control.Control'); +goog.require('ol.dom'); +goog.require('ol.css'); + + +/** + * @classdesc + * Provides a button that when clicked fills up the full screen with the map. + * The full screen source element is by default the element containing the map viewport unless + * overriden by providing the `source` option. In which case, the dom + * element introduced using this parameter will be displayed in full screen. + * + * When in full screen mode, a close button is shown to exit full screen mode. + * The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to + * toggle the map in full screen mode. + * + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.FullScreenOptions=} opt_options Options. + * @api stable + */ +ol.control.FullScreen = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {string} + */ + this.cssClassName_ = options.className !== undefined ? options.className : + 'ol-full-screen'; + + var label = options.label !== undefined ? options.label : '\u2922'; + + /** + * @private + * @type {Node} + */ + this.labelNode_ = typeof label === 'string' ? + document.createTextNode(label) : label; + + var labelActive = options.labelActive !== undefined ? options.labelActive : '\u00d7'; + + /** + * @private + * @type {Node} + */ + this.labelActiveNode_ = typeof labelActive === 'string' ? + document.createTextNode(labelActive) : labelActive; + + var tipLabel = options.tipLabel ? options.tipLabel : 'Toggle full-screen'; + var button = document.createElement('button'); + button.className = this.cssClassName_ + '-' + ol.control.FullScreen.isFullScreen(); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild(this.labelNode_); + + ol.events.listen(button, ol.events.EventType.CLICK, + this.handleClick_, this); + + var cssClasses = this.cssClassName_ + ' ' + ol.css.CLASS_UNSELECTABLE + + ' ' + ol.css.CLASS_CONTROL + ' ' + + (!ol.control.FullScreen.isFullScreenSupported() ? ol.css.CLASS_UNSUPPORTED : ''); + var element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(button); + + ol.control.Control.call(this, { + element: element, + target: options.target + }); + + /** + * @private + * @type {boolean} + */ + this.keys_ = options.keys !== undefined ? options.keys : false; + + /** + * @private + * @type {Element|string|undefined} + */ + this.source_ = options.source; + +}; +ol.inherits(ol.control.FullScreen, ol.control.Control); + + +/** + * @param {Event} event The event to handle + * @private + */ +ol.control.FullScreen.prototype.handleClick_ = function(event) { + event.preventDefault(); + this.handleFullScreen_(); +}; + + +/** + * @private + */ +ol.control.FullScreen.prototype.handleFullScreen_ = function() { + if (!ol.control.FullScreen.isFullScreenSupported()) { + return; + } + var map = this.getMap(); + if (!map) { + return; + } + if (ol.control.FullScreen.isFullScreen()) { + ol.control.FullScreen.exitFullScreen(); + } else { + var element; + if (this.source_) { + element = typeof this.source_ === 'string' ? + document.getElementById(this.source_) : + this.source_; + } else { + element = map.getTargetElement(); + } + goog.asserts.assert(element, 'element should be defined'); + if (this.keys_) { + ol.control.FullScreen.requestFullScreenWithKeys(element); + + } else { + ol.control.FullScreen.requestFullScreen(element); + } + } +}; + + +/** + * @private + */ +ol.control.FullScreen.prototype.handleFullScreenChange_ = function() { + var button = this.element.firstElementChild; + var map = this.getMap(); + if (ol.control.FullScreen.isFullScreen()) { + button.className = this.cssClassName_ + '-true'; + ol.dom.replaceNode(this.labelActiveNode_, this.labelNode_); + } else { + button.className = this.cssClassName_ + '-false'; + ol.dom.replaceNode(this.labelNode_, this.labelActiveNode_); + } + if (map) { + map.updateSize(); + } +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.control.FullScreen.prototype.setMap = function(map) { + ol.control.Control.prototype.setMap.call(this, map); + if (map) { + this.listenerKeys.push(ol.events.listen(ol.global.document, + ol.control.FullScreen.getChangeType_(), + this.handleFullScreenChange_, this) + ); + } +}; + +/** + * @return {boolean} Fullscreen is supported by the current platform. + */ +ol.control.FullScreen.isFullScreenSupported = function() { + var body = document.body; + return !!( + body.webkitRequestFullscreen || + (body.mozRequestFullScreen && document.mozFullScreenEnabled) || + (body.msRequestFullscreen && document.msFullscreenEnabled) || + (body.requestFullscreen && document.fullscreenEnabled) + ); +}; + +/** + * @return {boolean} Element is currently in fullscreen. + */ +ol.control.FullScreen.isFullScreen = function() { + return !!( + document.webkitIsFullScreen || document.mozFullScreen || + document.msFullscreenElement || document.fullscreenElement + ); +}; + +/** + * Request to fullscreen an element. + * @param {Node} element Element to request fullscreen + */ +ol.control.FullScreen.requestFullScreen = function(element) { + if (element.requestFullscreen) { + element.requestFullscreen(); + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen(); + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(); + } +}; + +/** + * Request to fullscreen an element with keyboard input. + * @param {Node} element Element to request fullscreen + */ +ol.control.FullScreen.requestFullScreenWithKeys = function(element) { + if (element.mozRequestFullScreenWithKeys) { + element.mozRequestFullScreenWithKeys(); + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } else { + ol.control.FullScreen.requestFullScreen(element); + } +}; + +/** + * Exit fullscreen. + */ +ol.control.FullScreen.exitFullScreen = function() { + if (document.exitFullscreen) { + document.exitFullscreen(); + } else if (document.msExitFullscreen) { + document.msExitFullscreen(); + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen(); + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen(); + } +}; + +/** + * @return {string} Change type. + * @private + */ +ol.control.FullScreen.getChangeType_ = (function() { + var changeType; + return function() { + if (!changeType) { + var body = document.body; + if (body.webkitRequestFullscreen) { + changeType = 'webkitfullscreenchange'; + } else if (body.mozRequestFullScreen) { + changeType = 'mozfullscreenchange'; + } else if (body.msRequestFullscreen) { + changeType = 'MSFullscreenChange'; + } else if (body.requestFullscreen) { + changeType = 'fullscreenchange'; + } + } + return changeType; + }; +})(); + +// FIXME should listen on appropriate pane, once it is defined + +goog.provide('ol.control.MousePosition'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.Object'); +goog.require('ol.control.Control'); +goog.require('ol.proj'); +goog.require('ol.proj.Projection'); + + +/** + * @enum {string} + */ +ol.control.MousePositionProperty = { + PROJECTION: 'projection', + COORDINATE_FORMAT: 'coordinateFormat' +}; + + +/** + * @classdesc + * A control to show the 2D coordinates of the mouse cursor. By default, these + * are in the view projection, but can be in any supported projection. + * By default the control is shown in the top right corner of the map, but this + * can be changed by using the css selector `.ol-mouse-position`. + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.MousePositionOptions=} opt_options Mouse position + * options. + * @api stable + */ +ol.control.MousePosition = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + var element = document.createElement('DIV'); + element.className = options.className !== undefined ? options.className : 'ol-mouse-position'; + + var render = options.render ? + options.render : ol.control.MousePosition.render; + + ol.control.Control.call(this, { + element: element, + render: render, + target: options.target + }); + + ol.events.listen(this, + ol.Object.getChangeEventType(ol.control.MousePositionProperty.PROJECTION), + this.handleProjectionChanged_, this); + + if (options.coordinateFormat) { + this.setCoordinateFormat(options.coordinateFormat); + } + if (options.projection) { + this.setProjection(ol.proj.get(options.projection)); + } + + /** + * @private + * @type {string} + */ + this.undefinedHTML_ = options.undefinedHTML !== undefined ? options.undefinedHTML : ''; + + /** + * @private + * @type {string} + */ + this.renderedHTML_ = element.innerHTML; + + /** + * @private + * @type {ol.proj.Projection} + */ + this.mapProjection_ = null; + + /** + * @private + * @type {?ol.TransformFunction} + */ + this.transform_ = null; + + /** + * @private + * @type {ol.Pixel} + */ + this.lastMouseMovePixel_ = null; + +}; +ol.inherits(ol.control.MousePosition, ol.control.Control); + + +/** + * Update the mouseposition element. + * @param {ol.MapEvent} mapEvent Map event. + * @this {ol.control.MousePosition} + * @api + */ +ol.control.MousePosition.render = function(mapEvent) { + var frameState = mapEvent.frameState; + if (!frameState) { + this.mapProjection_ = null; + } else { + if (this.mapProjection_ != frameState.viewState.projection) { + this.mapProjection_ = frameState.viewState.projection; + this.transform_ = null; + } + } + this.updateHTML_(this.lastMouseMovePixel_); +}; + + +/** + * @private + */ +ol.control.MousePosition.prototype.handleProjectionChanged_ = function() { + this.transform_ = null; +}; + + +/** + * Return the coordinate format type used to render the current position or + * undefined. + * @return {ol.CoordinateFormatType|undefined} The format to render the current + * position in. + * @observable + * @api stable + */ +ol.control.MousePosition.prototype.getCoordinateFormat = function() { + return /** @type {ol.CoordinateFormatType|undefined} */ ( + this.get(ol.control.MousePositionProperty.COORDINATE_FORMAT)); +}; + + +/** + * Return the projection that is used to report the mouse position. + * @return {ol.proj.Projection|undefined} The projection to report mouse + * position in. + * @observable + * @api stable + */ +ol.control.MousePosition.prototype.getProjection = function() { + return /** @type {ol.proj.Projection|undefined} */ ( + this.get(ol.control.MousePositionProperty.PROJECTION)); +}; + + +/** + * @param {Event} event Browser event. + * @protected + */ +ol.control.MousePosition.prototype.handleMouseMove = function(event) { + var map = this.getMap(); + this.lastMouseMovePixel_ = map.getEventPixel(event); + this.updateHTML_(this.lastMouseMovePixel_); +}; + + +/** + * @param {Event} event Browser event. + * @protected + */ +ol.control.MousePosition.prototype.handleMouseOut = function(event) { + this.updateHTML_(null); + this.lastMouseMovePixel_ = null; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.control.MousePosition.prototype.setMap = function(map) { + ol.control.Control.prototype.setMap.call(this, map); + if (map) { + var viewport = map.getViewport(); + this.listenerKeys.push( + ol.events.listen(viewport, ol.events.EventType.MOUSEMOVE, + this.handleMouseMove, this), + ol.events.listen(viewport, ol.events.EventType.MOUSEOUT, + this.handleMouseOut, this) + ); + } +}; + + +/** + * Set the coordinate format type used to render the current position. + * @param {ol.CoordinateFormatType} format The format to render the current + * position in. + * @observable + * @api stable + */ +ol.control.MousePosition.prototype.setCoordinateFormat = function(format) { + this.set(ol.control.MousePositionProperty.COORDINATE_FORMAT, format); +}; + + +/** + * Set the projection that is used to report the mouse position. + * @param {ol.proj.Projection} projection The projection to report mouse + * position in. + * @observable + * @api stable + */ +ol.control.MousePosition.prototype.setProjection = function(projection) { + this.set(ol.control.MousePositionProperty.PROJECTION, projection); +}; + + +/** + * @param {?ol.Pixel} pixel Pixel. + * @private + */ +ol.control.MousePosition.prototype.updateHTML_ = function(pixel) { + var html = this.undefinedHTML_; + if (pixel && this.mapProjection_) { + if (!this.transform_) { + var projection = this.getProjection(); + if (projection) { + this.transform_ = ol.proj.getTransformFromProjections( + this.mapProjection_, projection); + } else { + this.transform_ = ol.proj.identityTransform; + } + } + var map = this.getMap(); + var coordinate = map.getCoordinateFromPixel(pixel); + if (coordinate) { + this.transform_(coordinate, coordinate); + var coordinateFormat = this.getCoordinateFormat(); + if (coordinateFormat) { + html = coordinateFormat(coordinate); + } else { + html = coordinate.toString(); + } + } + } + if (!this.renderedHTML_ || html != this.renderedHTML_) { + this.element.innerHTML = html; + this.renderedHTML_ = html; + } +}; + +// Copyright 2010 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview A global registry for entry points into a program, + * so that they can be instrumented. Each module should register their + * entry points with this registry. Designed to be compiled out + * if no instrumentation is requested. + * + * Entry points may be registered before or after a call to + * goog.debug.entryPointRegistry.monitorAll. If an entry point is registered + * later, the existing monitor will instrument the new entry point. + * + * @author nicksantos@google.com (Nick Santos) + */ + +goog.provide('goog.debug.EntryPointMonitor'); +goog.provide('goog.debug.entryPointRegistry'); + +goog.require('goog.asserts'); + + + +/** + * @interface + */ +goog.debug.EntryPointMonitor = function() {}; + + +/** + * Instruments a function. + * + * @param {!Function} fn A function to instrument. + * @return {!Function} The instrumented function. + */ +goog.debug.EntryPointMonitor.prototype.wrap; + + +/** + * Try to remove an instrumentation wrapper created by this monitor. + * If the function passed to unwrap is not a wrapper created by this + * monitor, then we will do nothing. + * + * Notice that some wrappers may not be unwrappable. For example, if other + * monitors have applied their own wrappers, then it will be impossible to + * unwrap them because their wrappers will have captured our wrapper. + * + * So it is important that entry points are unwrapped in the reverse + * order that they were wrapped. + * + * @param {!Function} fn A function to unwrap. + * @return {!Function} The unwrapped function, or {@code fn} if it was not + * a wrapped function created by this monitor. + */ +goog.debug.EntryPointMonitor.prototype.unwrap; + + +/** + * An array of entry point callbacks. + * @type {!Array<function(!Function)>} + * @private + */ +goog.debug.entryPointRegistry.refList_ = []; + + +/** + * Monitors that should wrap all the entry points. + * @type {!Array<!goog.debug.EntryPointMonitor>} + * @private + */ +goog.debug.entryPointRegistry.monitors_ = []; + + +/** + * Whether goog.debug.entryPointRegistry.monitorAll has ever been called. + * Checking this allows the compiler to optimize out the registrations. + * @type {boolean} + * @private + */ +goog.debug.entryPointRegistry.monitorsMayExist_ = false; + + +/** + * Register an entry point with this module. + * + * The entry point will be instrumented when a monitor is passed to + * goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the + * entry point is instrumented immediately. + * + * @param {function(!Function)} callback A callback function which is called + * with a transforming function to instrument the entry point. The callback + * is responsible for wrapping the relevant entry point with the + * transforming function. + */ +goog.debug.entryPointRegistry.register = function(callback) { + // Don't use push(), so that this can be compiled out. + goog.debug.entryPointRegistry + .refList_[goog.debug.entryPointRegistry.refList_.length] = callback; + // If no one calls monitorAll, this can be compiled out. + if (goog.debug.entryPointRegistry.monitorsMayExist_) { + var monitors = goog.debug.entryPointRegistry.monitors_; + for (var i = 0; i < monitors.length; i++) { + callback(goog.bind(monitors[i].wrap, monitors[i])); + } + } +}; + + +/** + * Configures a monitor to wrap all entry points. + * + * Entry points that have already been registered are immediately wrapped by + * the monitor. When an entry point is registered in the future, it will also + * be wrapped by the monitor when it is registered. + * + * @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor. + */ +goog.debug.entryPointRegistry.monitorAll = function(monitor) { + goog.debug.entryPointRegistry.monitorsMayExist_ = true; + var transformer = goog.bind(monitor.wrap, monitor); + for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) { + goog.debug.entryPointRegistry.refList_[i](transformer); + } + goog.debug.entryPointRegistry.monitors_.push(monitor); +}; + + +/** + * Try to unmonitor all the entry points that have already been registered. If + * an entry point is registered in the future, it will not be wrapped by the + * monitor when it is registered. Note that this may fail if the entry points + * have additional wrapping. + * + * @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap + * the entry points. + * @throws {Error} If the monitor is not the most recently configured monitor. + */ +goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) { + var monitors = goog.debug.entryPointRegistry.monitors_; + goog.asserts.assert( + monitor == monitors[monitors.length - 1], + 'Only the most recent monitor can be unwrapped.'); + var transformer = goog.bind(monitor.unwrap, monitor); + for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) { + goog.debug.entryPointRegistry.refList_[i](transformer); + } + monitors.length--; +}; + +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Defines the goog.dom.TagName enum. This enumerates + * all HTML tag names specified in either the the W3C HTML 4.01 index of + * elements or the HTML5 draft specification. + * + * References: + * http://www.w3.org/TR/html401/index/elements.html + * http://dev.w3.org/html5/spec/section-index.html + * + */ +goog.provide('goog.dom.TagName'); + + +/** + * Enum of all html tag names specified by the W3C HTML4.01 and HTML5 + * specifications. + * @enum {string} + */ +goog.dom.TagName = { + A: 'A', + ABBR: 'ABBR', + ACRONYM: 'ACRONYM', + ADDRESS: 'ADDRESS', + APPLET: 'APPLET', + AREA: 'AREA', + ARTICLE: 'ARTICLE', + ASIDE: 'ASIDE', + AUDIO: 'AUDIO', + B: 'B', + BASE: 'BASE', + BASEFONT: 'BASEFONT', + BDI: 'BDI', + BDO: 'BDO', + BIG: 'BIG', + BLOCKQUOTE: 'BLOCKQUOTE', + BODY: 'BODY', + BR: 'BR', + BUTTON: 'BUTTON', + CANVAS: 'CANVAS', + CAPTION: 'CAPTION', + CENTER: 'CENTER', + CITE: 'CITE', + CODE: 'CODE', + COL: 'COL', + COLGROUP: 'COLGROUP', + COMMAND: 'COMMAND', + DATA: 'DATA', + DATALIST: 'DATALIST', + DD: 'DD', + DEL: 'DEL', + DETAILS: 'DETAILS', + DFN: 'DFN', + DIALOG: 'DIALOG', + DIR: 'DIR', + DIV: 'DIV', + DL: 'DL', + DT: 'DT', + EM: 'EM', + EMBED: 'EMBED', + FIELDSET: 'FIELDSET', + FIGCAPTION: 'FIGCAPTION', + FIGURE: 'FIGURE', + FONT: 'FONT', + FOOTER: 'FOOTER', + FORM: 'FORM', + FRAME: 'FRAME', + FRAMESET: 'FRAMESET', + H1: 'H1', + H2: 'H2', + H3: 'H3', + H4: 'H4', + H5: 'H5', + H6: 'H6', + HEAD: 'HEAD', + HEADER: 'HEADER', + HGROUP: 'HGROUP', + HR: 'HR', + HTML: 'HTML', + I: 'I', + IFRAME: 'IFRAME', + IMG: 'IMG', + INPUT: 'INPUT', + INS: 'INS', + ISINDEX: 'ISINDEX', + KBD: 'KBD', + KEYGEN: 'KEYGEN', + LABEL: 'LABEL', + LEGEND: 'LEGEND', + LI: 'LI', + LINK: 'LINK', + MAP: 'MAP', + MARK: 'MARK', + MATH: 'MATH', + MENU: 'MENU', + META: 'META', + METER: 'METER', + NAV: 'NAV', + NOFRAMES: 'NOFRAMES', + NOSCRIPT: 'NOSCRIPT', + OBJECT: 'OBJECT', + OL: 'OL', + OPTGROUP: 'OPTGROUP', + OPTION: 'OPTION', + OUTPUT: 'OUTPUT', + P: 'P', + PARAM: 'PARAM', + PRE: 'PRE', + PROGRESS: 'PROGRESS', + Q: 'Q', + RP: 'RP', + RT: 'RT', + RUBY: 'RUBY', + S: 'S', + SAMP: 'SAMP', + SCRIPT: 'SCRIPT', + SECTION: 'SECTION', + SELECT: 'SELECT', + SMALL: 'SMALL', + SOURCE: 'SOURCE', + SPAN: 'SPAN', + STRIKE: 'STRIKE', + STRONG: 'STRONG', + STYLE: 'STYLE', + SUB: 'SUB', + SUMMARY: 'SUMMARY', + SUP: 'SUP', + SVG: 'SVG', + TABLE: 'TABLE', + TBODY: 'TBODY', + TD: 'TD', + TEMPLATE: 'TEMPLATE', + TEXTAREA: 'TEXTAREA', + TFOOT: 'TFOOT', + TH: 'TH', + THEAD: 'THEAD', + TIME: 'TIME', + TITLE: 'TITLE', + TR: 'TR', + TRACK: 'TRACK', + TT: 'TT', + U: 'U', + UL: 'UL', + VAR: 'VAR', + VIDEO: 'VIDEO', + WBR: 'WBR' +}; + +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Utilities for creating functions. Loosely inspired by the + * java classes: http://goo.gl/GM0Hmu and http://goo.gl/6k7nI8. + * + * @author nicksantos@google.com (Nick Santos) + */ + + +goog.provide('goog.functions'); + + +/** + * Creates a function that always returns the same value. + * @param {T} retValue The value to return. + * @return {function():T} The new function. + * @template T + */ +goog.functions.constant = function(retValue) { + return function() { return retValue; }; +}; + + +/** + * Always returns false. + * @type {function(...): boolean} + */ +goog.functions.FALSE = goog.functions.constant(false); + + +/** + * Always returns true. + * @type {function(...): boolean} + */ +goog.functions.TRUE = goog.functions.constant(true); + + +/** + * Always returns NULL. + * @type {function(...): null} + */ +goog.functions.NULL = goog.functions.constant(null); + + +/** + * A simple function that returns the first argument of whatever is passed + * into it. + * @param {T=} opt_returnValue The single value that will be returned. + * @param {...*} var_args Optional trailing arguments. These are ignored. + * @return {T} The first argument passed in, or undefined if nothing was passed. + * @template T + */ +goog.functions.identity = function(opt_returnValue, var_args) { + return opt_returnValue; +}; + + +/** + * Creates a function that always throws an error with the given message. + * @param {string} message The error message. + * @return {!Function} The error-throwing function. + */ +goog.functions.error = function(message) { + return function() { throw Error(message); }; +}; + + +/** + * Creates a function that throws the given object. + * @param {*} err An object to be thrown. + * @return {!Function} The error-throwing function. + */ +goog.functions.fail = function(err) { + return function() { throw err; }; +}; + + +/** + * Given a function, create a function that keeps opt_numArgs arguments and + * silently discards all additional arguments. + * @param {Function} f The original function. + * @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0. + * @return {!Function} A version of f that only keeps the first opt_numArgs + * arguments. + */ +goog.functions.lock = function(f, opt_numArgs) { + opt_numArgs = opt_numArgs || 0; + return function() { + return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs)); + }; +}; + + +/** + * Creates a function that returns its nth argument. + * @param {number} n The position of the return argument. + * @return {!Function} A new function. + */ +goog.functions.nth = function(n) { + return function() { return arguments[n]; }; +}; + + +/** + * Like goog.partial(), except that arguments are added after arguments to the + * returned function. + * + * Usage: + * function f(arg1, arg2, arg3, arg4) { ... } + * var g = goog.functions.partialRight(f, arg3, arg4); + * g(arg1, arg2); + * + * @param {!Function} fn A function to partially apply. + * @param {...*} var_args Additional arguments that are partially applied to fn + * at the end. + * @return {!Function} A partially-applied form of the function goog.partial() + * was invoked as a method of. + */ +goog.functions.partialRight = function(fn, var_args) { + var rightArgs = Array.prototype.slice.call(arguments, 1); + return function() { + var newArgs = Array.prototype.slice.call(arguments); + newArgs.push.apply(newArgs, rightArgs); + return fn.apply(this, newArgs); + }; +}; + + +/** + * Given a function, create a new function that swallows its return value + * and replaces it with a new one. + * @param {Function} f A function. + * @param {T} retValue A new return value. + * @return {function(...?):T} A new function. + * @template T + */ +goog.functions.withReturnValue = function(f, retValue) { + return goog.functions.sequence(f, goog.functions.constant(retValue)); +}; + + +/** + * Creates a function that returns whether its argument equals the given value. + * + * Example: + * var key = goog.object.findKey(obj, goog.functions.equalTo('needle')); + * + * @param {*} value The value to compare to. + * @param {boolean=} opt_useLooseComparison Whether to use a loose (==) + * comparison rather than a strict (===) one. Defaults to false. + * @return {function(*):boolean} The new function. + */ +goog.functions.equalTo = function(value, opt_useLooseComparison) { + return function(other) { + return opt_useLooseComparison ? (value == other) : (value === other); + }; +}; + + +/** + * Creates the composition of the functions passed in. + * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)). + * @param {function(...?):T} fn The final function. + * @param {...Function} var_args A list of functions. + * @return {function(...?):T} The composition of all inputs. + * @template T + */ +goog.functions.compose = function(fn, var_args) { + var functions = arguments; + var length = functions.length; + return function() { + var result; + if (length) { + result = functions[length - 1].apply(this, arguments); + } + + for (var i = length - 2; i >= 0; i--) { + result = functions[i].call(this, result); + } + return result; + }; +}; + + +/** + * Creates a function that calls the functions passed in in sequence, and + * returns the value of the last function. For example, + * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x). + * @param {...Function} var_args A list of functions. + * @return {!Function} A function that calls all inputs in sequence. + */ +goog.functions.sequence = function(var_args) { + var functions = arguments; + var length = functions.length; + return function() { + var result; + for (var i = 0; i < length; i++) { + result = functions[i].apply(this, arguments); + } + return result; + }; +}; + + +/** + * Creates a function that returns true if each of its components evaluates + * to true. The components are evaluated in order, and the evaluation will be + * short-circuited as soon as a function returns false. + * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x). + * @param {...Function} var_args A list of functions. + * @return {function(...?):boolean} A function that ANDs its component + * functions. + */ +goog.functions.and = function(var_args) { + var functions = arguments; + var length = functions.length; + return function() { + for (var i = 0; i < length; i++) { + if (!functions[i].apply(this, arguments)) { + return false; + } + } + return true; + }; +}; + + +/** + * Creates a function that returns true if any of its components evaluates + * to true. The components are evaluated in order, and the evaluation will be + * short-circuited as soon as a function returns true. + * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x). + * @param {...Function} var_args A list of functions. + * @return {function(...?):boolean} A function that ORs its component + * functions. + */ +goog.functions.or = function(var_args) { + var functions = arguments; + var length = functions.length; + return function() { + for (var i = 0; i < length; i++) { + if (functions[i].apply(this, arguments)) { + return true; + } + } + return false; + }; +}; + + +/** + * Creates a function that returns the Boolean opposite of a provided function. + * For example, (goog.functions.not(f))(x) is equivalent to !f(x). + * @param {!Function} f The original function. + * @return {function(...?):boolean} A function that delegates to f and returns + * opposite. + */ +goog.functions.not = function(f) { + return function() { return !f.apply(this, arguments); }; +}; + + +/** + * Generic factory function to construct an object given the constructor + * and the arguments. Intended to be bound to create object factories. + * + * Example: + * + * var factory = goog.partial(goog.functions.create, Class); + * + * @param {function(new:T, ...)} constructor The constructor for the Object. + * @param {...*} var_args The arguments to be passed to the constructor. + * @return {T} A new instance of the class given in {@code constructor}. + * @template T + */ +goog.functions.create = function(constructor, var_args) { + /** + * @constructor + * @final + */ + var temp = function() {}; + temp.prototype = constructor.prototype; + + // obj will have constructor's prototype in its chain and + // 'obj instanceof constructor' will be true. + var obj = new temp(); + + // obj is initialized by constructor. + // arguments is only array-like so lacks shift(), but can be used with + // the Array prototype function. + constructor.apply(obj, Array.prototype.slice.call(arguments, 1)); + return obj; +}; + + +/** + * @define {boolean} Whether the return value cache should be used. + * This should only be used to disable caches when testing. + */ +goog.define('goog.functions.CACHE_RETURN_VALUE', true); + + +/** + * Gives a wrapper function that caches the return value of a parameterless + * function when first called. + * + * When called for the first time, the given function is called and its + * return value is cached (thus this is only appropriate for idempotent + * functions). Subsequent calls will return the cached return value. This + * allows the evaluation of expensive functions to be delayed until first used. + * + * To cache the return values of functions with parameters, see goog.memoize. + * + * @param {!function():T} fn A function to lazily evaluate. + * @return {!function():T} A wrapped version the function. + * @template T + */ +goog.functions.cacheReturnValue = function(fn) { + var called = false; + var value; + + return function() { + if (!goog.functions.CACHE_RETURN_VALUE) { + return fn(); + } + + if (!called) { + value = fn(); + called = true; + } + + return value; + }; +}; + + +/** + * Wraps a function to allow it to be called, at most, once. All + * additional calls are no-ops. + * + * This is particularly useful for initialization functions + * that should be called, at most, once. + * + * @param {function():*} f Function to call. + * @return {function():undefined} Wrapped function. + */ +goog.functions.once = function(f) { + // Keep a reference to the function that we null out when we're done with + // it -- that way, the function can be GC'd when we're done with it. + var inner = f; + return function() { + if (inner) { + var tmp = inner; + inner = null; + tmp(); + } + }; +}; + + +/** + * Wraps a function to allow it to be called, at most, once for each sequence of + * calls fired repeatedly so long as they are fired less than a specified + * interval apart (in milliseconds). Whether it receives one signal or multiple, + * it will always wait until a full interval has elapsed since the last signal + * before performing the action, passing the arguments from the last call of the + * debouncing decorator into the decorated function. + * + * This is particularly useful for bulking up repeated user actions (e.g. only + * refreshing a view once a user finishes typing rather than updating with every + * keystroke). For more stateful debouncing with support for pausing, resuming, + * and canceling debounced actions, use {@code goog.async.Debouncer}. + * + * @param {function(this:SCOPE, ...?)} f Function to call. + * @param {number} interval Interval over which to debounce. The function will + * only be called after the full interval has elapsed since the last call. + * @param {SCOPE=} opt_scope Object in whose scope to call the function. + * @return {function(...?): undefined} Wrapped function. + * @template SCOPE + */ +goog.functions.debounce = function(f, interval, opt_scope) { + if (opt_scope) { + f = goog.bind(f, opt_scope); + } + var timeout = null; + return /** @type {function(...?)} */ (function(var_args) { + goog.global.clearTimeout(timeout); + var args = arguments; + timeout = + goog.global.setTimeout(function() { f.apply(null, args); }, interval); + }); +}; + + +/** + * Wraps a function to allow it to be called, at most, once per interval + * (specified in milliseconds). If it is called multiple times while it is + * waiting, it will only perform the action once at the end of the interval, + * passing the arguments from the last call of the throttling decorator into the + * decorated function. + * + * This is particularly useful for limiting repeated user requests (e.g. + * preventing a user from spamming a server with frequent view refreshes). For + * more stateful throttling with support for pausing, resuming, and canceling + * throttled actions, use {@code goog.async.Throttle}. + * + * @param {function(this:SCOPE, ...?)} f Function to call. + * @param {number} interval Interval over which to throttle. The function can + * only be called once per interval. + * @param {SCOPE=} opt_scope Object in whose scope to call the function. + * @return {function(...?): undefined} Wrapped function. + * @template SCOPE + */ +goog.functions.throttle = function(f, interval, opt_scope) { + if (opt_scope) { + f = goog.bind(f, opt_scope); + } + var timeout = null; + var shouldFire = false; + var args = []; + + var handleTimeout = function() { + timeout = null; + if (shouldFire) { + shouldFire = false; + fire(); + } + }; + + var fire = function() { + timeout = goog.global.setTimeout(handleTimeout, interval); + f.apply(null, args); + }; + + return /** @type {function(...?)} */ (function(var_args) { + args = arguments; + if (!timeout) { + fire(); + } else { + shouldFire = true; + } + }); +}; + +// Copyright 2013 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Provides a function to schedule running a function as soon + * as possible after the current JS execution stops and yields to the event + * loop. + * + */ + +goog.provide('goog.async.nextTick'); +goog.provide('goog.async.throwException'); + +goog.require('goog.debug.entryPointRegistry'); +goog.require('goog.dom.TagName'); +goog.require('goog.functions'); +goog.require('goog.labs.userAgent.browser'); +goog.require('goog.labs.userAgent.engine'); + + +/** + * Throw an item without interrupting the current execution context. For + * example, if processing a group of items in a loop, sometimes it is useful + * to report an error while still allowing the rest of the batch to be + * processed. + * @param {*} exception + */ +goog.async.throwException = function(exception) { + // Each throw needs to be in its own context. + goog.global.setTimeout(function() { throw exception; }, 0); +}; + + +/** + * Fires the provided callbacks as soon as possible after the current JS + * execution context. setTimeout(…, 0) takes at least 4ms when called from + * within another setTimeout(…, 0) for legacy reasons. + * + * This will not schedule the callback as a microtask (i.e. a task that can + * preempt user input or networking callbacks). It is meant to emulate what + * setTimeout(_, 0) would do if it were not throttled. If you desire microtask + * behavior, use {@see goog.Promise} instead. + * + * @param {function(this:SCOPE)} callback Callback function to fire as soon as + * possible. + * @param {SCOPE=} opt_context Object in whose scope to call the listener. + * @param {boolean=} opt_useSetImmediate Avoid the IE workaround that + * ensures correctness at the cost of speed. See comments for details. + * @template SCOPE + */ +goog.async.nextTick = function(callback, opt_context, opt_useSetImmediate) { + var cb = callback; + if (opt_context) { + cb = goog.bind(callback, opt_context); + } + cb = goog.async.nextTick.wrapCallback_(cb); + // Note we do allow callers to also request setImmediate if they are willing + // to accept the possible tradeoffs of incorrectness in exchange for speed. + // The IE fallback of readystate change is much slower. See useSetImmediate_ + // for details. + if (goog.isFunction(goog.global.setImmediate) && + (opt_useSetImmediate || goog.async.nextTick.useSetImmediate_())) { + goog.global.setImmediate(cb); + return; + } + + // Look for and cache the custom fallback version of setImmediate. + if (!goog.async.nextTick.setImmediate_) { + goog.async.nextTick.setImmediate_ = + goog.async.nextTick.getSetImmediateEmulator_(); + } + goog.async.nextTick.setImmediate_(cb); +}; + + +/** + * Returns whether should use setImmediate implementation currently on window. + * + * window.setImmediate was introduced and currently only supported by IE10+, + * but due to a bug in the implementation it is not guaranteed that + * setImmediate is faster than setTimeout nor that setImmediate N is before + * setImmediate N+1. That is why we do not use the native version if + * available. We do, however, call setImmediate if it is a non-native function + * because that indicates that it has been replaced by goog.testing.MockClock + * which we do want to support. + * See + * http://connect.microsoft.com/IE/feedback/details/801823/setimmediate-and-messagechannel-are-broken-in-ie10 + * + * @return {boolean} Whether to use the implementation of setImmediate defined + * on Window. + * @private + */ +goog.async.nextTick.useSetImmediate_ = function() { + // Not a browser environment. + if (!goog.global.Window || !goog.global.Window.prototype) { + return true; + } + + // MS Edge has window.setImmediate natively, but it's not on Window.prototype. + // Also, there's no clean way to detect if the goog.global.setImmediate has + // been replaced by mockClock as its replacement also shows up as "[native + // code]" when using toString. Therefore, just always use + // goog.global.setImmediate for Edge. It's unclear if it suffers the same + // issues as IE10/11, but based on + // https://dev.modern.ie/testdrive/demos/setimmediatesorting/ + // it seems they've been working to ensure it's WAI. + if (goog.labs.userAgent.browser.isEdge() || + goog.global.Window.prototype.setImmediate != goog.global.setImmediate) { + // Something redefined setImmediate in which case we decide to use it (This + // is so that we use the mockClock setImmediate). + return true; + } + + return false; +}; + + +/** + * Cache for the setImmediate implementation. + * @type {function(function())} + * @private + */ +goog.async.nextTick.setImmediate_; + + +/** + * Determines the best possible implementation to run a function as soon as + * the JS event loop is idle. + * @return {function(function())} The "setImmediate" implementation. + * @private + */ +goog.async.nextTick.getSetImmediateEmulator_ = function() { + // Create a private message channel and use it to postMessage empty messages + // to ourselves. + var Channel = goog.global['MessageChannel']; + // If MessageChannel is not available and we are in a browser, implement + // an iframe based polyfill in browsers that have postMessage and + // document.addEventListener. The latter excludes IE8 because it has a + // synchronous postMessage implementation. + if (typeof Channel === 'undefined' && typeof window !== 'undefined' && + window.postMessage && window.addEventListener && + // Presto (The old pre-blink Opera engine) has problems with iframes + // and contentWindow. + !goog.labs.userAgent.engine.isPresto()) { + /** @constructor */ + Channel = function() { + // Make an empty, invisible iframe. + var iframe = /** @type {!HTMLIFrameElement} */ ( + document.createElement(goog.dom.TagName.IFRAME)); + iframe.style.display = 'none'; + iframe.src = ''; + document.documentElement.appendChild(iframe); + var win = iframe.contentWindow; + var doc = win.document; + doc.open(); + doc.write(''); + doc.close(); + // Do not post anything sensitive over this channel, as the workaround for + // pages with file: origin could allow that information to be modified or + // intercepted. + var message = 'callImmediate' + Math.random(); + // The same origin policy rejects attempts to postMessage from file: urls + // unless the origin is '*'. + // TODO(b/16335441): Use '*' origin for data: and other similar protocols. + var origin = win.location.protocol == 'file:' ? + '*' : + win.location.protocol + '//' + win.location.host; + var onmessage = goog.bind(function(e) { + // Validate origin and message to make sure that this message was + // intended for us. If the origin is set to '*' (see above) only the + // message needs to match since, for example, '*' != 'file://'. Allowing + // the wildcard is ok, as we are not concerned with security here. + if ((origin != '*' && e.origin != origin) || e.data != message) { + return; + } + this['port1'].onmessage(); + }, this); + win.addEventListener('message', onmessage, false); + this['port1'] = {}; + this['port2'] = { + postMessage: function() { win.postMessage(message, origin); } + }; + }; + } + if (typeof Channel !== 'undefined' && (!goog.labs.userAgent.browser.isIE())) { + // Exclude all of IE due to + // http://codeforhire.com/2013/09/21/setimmediate-and-messagechannel-broken-on-internet-explorer-10/ + // which allows starving postMessage with a busy setTimeout loop. + // This currently affects IE10 and IE11 which would otherwise be able + // to use the postMessage based fallbacks. + var channel = new Channel(); + // Use a fifo linked list to call callbacks in the right order. + var head = {}; + var tail = head; + channel['port1'].onmessage = function() { + if (goog.isDef(head.next)) { + head = head.next; + var cb = head.cb; + head.cb = null; + cb(); + } + }; + return function(cb) { + tail.next = {cb: cb}; + tail = tail.next; + channel['port2'].postMessage(0); + }; + } + // Implementation for IE6 to IE10: Script elements fire an asynchronous + // onreadystatechange event when inserted into the DOM. + if (typeof document !== 'undefined' && + 'onreadystatechange' in document.createElement(goog.dom.TagName.SCRIPT)) { + return function(cb) { + var script = document.createElement(goog.dom.TagName.SCRIPT); + script.onreadystatechange = function() { + // Clean up and call the callback. + script.onreadystatechange = null; + script.parentNode.removeChild(script); + script = null; + cb(); + cb = null; + }; + document.documentElement.appendChild(script); + }; + } + // Fall back to setTimeout with 0. In browsers this creates a delay of 5ms + // or more. + // NOTE(user): This fallback is used for IE11. + return function(cb) { goog.global.setTimeout(cb, 0); }; +}; + + +/** + * Helper function that is overrided to protect callbacks with entry point + * monitor if the application monitors entry points. + * @param {function()} callback Callback function to fire as soon as possible. + * @return {function()} The wrapped callback. + * @private + */ +goog.async.nextTick.wrapCallback_ = goog.functions.identity; + + +// Register the callback function as an entry point, so that it can be +// monitored for exception handling, etc. This has to be done in this file +// since it requires special code to handle all browsers. +goog.debug.entryPointRegistry.register( + /** + * @param {function(!Function): !Function} transformer The transforming + * function. + */ + function(transformer) { goog.async.nextTick.wrapCallback_ = transformer; }); + +// Based on https://github.com/Polymer/PointerEvents + +// Copyright (c) 2013 The Polymer Authors. All rights reserved. +// +// Redistribution and use 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +goog.provide('ol.pointer.PointerEvent'); + + +goog.require('ol.events'); +goog.require('ol.events.Event'); + + +/** + * A class for pointer events. + * + * This class is used as an abstraction for mouse events, + * touch events and even native pointer events. + * + * @constructor + * @extends {ol.events.Event} + * @param {string} type The type of the event to create. + * @param {Event} originalEvent The event. + * @param {Object.<string, ?>=} opt_eventDict An optional dictionary of + * initial event properties. + */ +ol.pointer.PointerEvent = function(type, originalEvent, opt_eventDict) { + ol.events.Event.call(this, type); + + /** + * @const + * @type {Event} + */ + this.originalEvent = originalEvent; + + var eventDict = opt_eventDict ? opt_eventDict : {}; + + /** + * @type {number} + */ + this.buttons = this.getButtons_(eventDict); + + /** + * @type {number} + */ + this.pressure = this.getPressure_(eventDict, this.buttons); + + // MouseEvent related properties + + /** + * @type {boolean} + */ + this.bubbles = 'bubbles' in eventDict ? eventDict['bubbles'] : false; + + /** + * @type {boolean} + */ + this.cancelable = 'cancelable' in eventDict ? eventDict['cancelable'] : false; + + /** + * @type {Object} + */ + this.view = 'view' in eventDict ? eventDict['view'] : null; + + /** + * @type {number} + */ + this.detail = 'detail' in eventDict ? eventDict['detail'] : null; + + /** + * @type {number} + */ + this.screenX = 'screenX' in eventDict ? eventDict['screenX'] : 0; + + /** + * @type {number} + */ + this.screenY = 'screenY' in eventDict ? eventDict['screenY'] : 0; + + /** + * @type {number} + */ + this.clientX = 'clientX' in eventDict ? eventDict['clientX'] : 0; + + /** + * @type {number} + */ + this.clientY = 'clientY' in eventDict ? eventDict['clientY'] : 0; + + /** + * @type {boolean} + */ + this.ctrlKey = 'ctrlKey' in eventDict ? eventDict['ctrlKey'] : false; + + /** + * @type {boolean} + */ + this.altKey = 'altKey' in eventDict ? eventDict['altKey'] : false; + + /** + * @type {boolean} + */ + this.shiftKey = 'shiftKey' in eventDict ? eventDict['shiftKey'] : false; + + /** + * @type {boolean} + */ + this.metaKey = 'metaKey' in eventDict ? eventDict['metaKey'] : false; + + /** + * @type {number} + */ + this.button = 'button' in eventDict ? eventDict['button'] : 0; + + /** + * @type {Node} + */ + this.relatedTarget = 'relatedTarget' in eventDict ? + eventDict['relatedTarget'] : null; + + // PointerEvent related properties + + /** + * @const + * @type {number} + */ + this.pointerId = 'pointerId' in eventDict ? eventDict['pointerId'] : 0; + + /** + * @type {number} + */ + this.width = 'width' in eventDict ? eventDict['width'] : 0; + + /** + * @type {number} + */ + this.height = 'height' in eventDict ? eventDict['height'] : 0; + + /** + * @type {number} + */ + this.tiltX = 'tiltX' in eventDict ? eventDict['tiltX'] : 0; + + /** + * @type {number} + */ + this.tiltY = 'tiltY' in eventDict ? eventDict['tiltY'] : 0; + + /** + * @type {string} + */ + this.pointerType = 'pointerType' in eventDict ? eventDict['pointerType'] : ''; + + /** + * @type {number} + */ + this.hwTimestamp = 'hwTimestamp' in eventDict ? eventDict['hwTimestamp'] : 0; + + /** + * @type {boolean} + */ + this.isPrimary = 'isPrimary' in eventDict ? eventDict['isPrimary'] : false; + + // keep the semantics of preventDefault + if (originalEvent.preventDefault) { + this.preventDefault = function() { + originalEvent.preventDefault(); + }; + } +}; +ol.inherits(ol.pointer.PointerEvent, ol.events.Event); + + +/** + * @private + * @param {Object.<string, ?>} eventDict The event dictionary. + * @return {number} Button indicator. + */ +ol.pointer.PointerEvent.prototype.getButtons_ = function(eventDict) { + // According to the w3c spec, + // http://www.w3.org/TR/DOM-Level-3-Events/#events-MouseEvent-button + // MouseEvent.button == 0 can mean either no mouse button depressed, or the + // left mouse button depressed. + // + // As of now, the only way to distinguish between the two states of + // MouseEvent.button is by using the deprecated MouseEvent.which property, as + // this maps mouse buttons to positive integers > 0, and uses 0 to mean that + // no mouse button is held. + // + // MouseEvent.which is derived from MouseEvent.button at MouseEvent creation, + // but initMouseEvent does not expose an argument with which to set + // MouseEvent.which. Calling initMouseEvent with a buttonArg of 0 will set + // MouseEvent.button == 0 and MouseEvent.which == 1, breaking the expectations + // of app developers. + // + // The only way to propagate the correct state of MouseEvent.which and + // MouseEvent.button to a new MouseEvent.button == 0 and MouseEvent.which == 0 + // is to call initMouseEvent with a buttonArg value of -1. + // + // This is fixed with DOM Level 4's use of buttons + var buttons; + if (eventDict.buttons || ol.pointer.PointerEvent.HAS_BUTTONS) { + buttons = eventDict.buttons; + } else { + switch (eventDict.which) { + case 1: buttons = 1; break; + case 2: buttons = 4; break; + case 3: buttons = 2; break; + default: buttons = 0; + } + } + return buttons; +}; + + +/** + * @private + * @param {Object.<string, ?>} eventDict The event dictionary. + * @param {number} buttons Button indicator. + * @return {number} The pressure. + */ +ol.pointer.PointerEvent.prototype.getPressure_ = function(eventDict, buttons) { + // Spec requires that pointers without pressure specified use 0.5 for down + // state and 0 for up state. + var pressure = 0; + if (eventDict.pressure) { + pressure = eventDict.pressure; + } else { + pressure = buttons ? 0.5 : 0; + } + return pressure; +}; + + +/** + * Is the `buttons` property supported? + * @type {boolean} + */ +ol.pointer.PointerEvent.HAS_BUTTONS = false; + + +/** + * Checks if the `buttons` property is supported. + */ +(function() { + try { + var ev = new MouseEvent('click', {buttons: 1}); + ol.pointer.PointerEvent.HAS_BUTTONS = ev.buttons === 1; + } catch (e) { + // pass + } +})(); + +goog.provide('ol.webgl'); +goog.provide('ol.webgl.WebGLContextEventType'); + + +/** + * @const + * @private + * @type {Array.<string>} + */ +ol.webgl.CONTEXT_IDS_ = [ + 'experimental-webgl', + 'webgl', + 'webkit-3d', + 'moz-webgl' +]; + + +/** + * @enum {string} + */ +ol.webgl.WebGLContextEventType = { + LOST: 'webglcontextlost', + RESTORED: 'webglcontextrestored' +}; + + +/** + * @param {HTMLCanvasElement} canvas Canvas. + * @param {Object=} opt_attributes Attributes. + * @return {WebGLRenderingContext} WebGL rendering context. + */ +ol.webgl.getContext = function(canvas, opt_attributes) { + var context, i, ii = ol.webgl.CONTEXT_IDS_.length; + for (i = 0; i < ii; ++i) { + try { + context = canvas.getContext(ol.webgl.CONTEXT_IDS_[i], opt_attributes); + if (context) { + return /** @type {!WebGLRenderingContext} */ (context); + } + } catch (e) { + // pass + } + } + return null; +}; + +goog.provide('ol.has'); + +goog.require('ol'); +goog.require('ol.dom'); +goog.require('ol.webgl'); + + +var ua = typeof navigator !== 'undefined' ? + navigator.userAgent.toLowerCase() : ''; + +/** + * User agent string says we are dealing with Firefox as browser. + * @type {boolean} + */ +ol.has.FIREFOX = ua.indexOf('firefox') !== -1; + +/** + * User agent string says we are dealing with Safari as browser. + * @type {boolean} + */ +ol.has.SAFARI = ua.indexOf('safari') !== -1 && ua.indexOf('chrom') === -1; + +/** + * User agent string says we are dealing with a Mac as platform. + * @type {boolean} + */ +ol.has.MAC = ua.indexOf('macintosh') !== -1; + + +/** + * The ratio between physical pixels and device-independent pixels + * (dips) on the device (`window.devicePixelRatio`). + * @const + * @type {number} + * @api stable + */ +ol.has.DEVICE_PIXEL_RATIO = ol.global.devicePixelRatio || 1; + + +/** + * True if the browser's Canvas implementation implements {get,set}LineDash. + * @type {boolean} + */ +ol.has.CANVAS_LINE_DASH = false; + + +/** + * True if both the library and browser support Canvas. Always `false` + * if `ol.ENABLE_CANVAS` is set to `false` at compile time. + * @const + * @type {boolean} + * @api stable + */ +ol.has.CANVAS = ol.ENABLE_CANVAS && ( + /** + * @return {boolean} Canvas supported. + */ + function() { + if (!('HTMLCanvasElement' in ol.global)) { + return false; + } + try { + var context = ol.dom.createCanvasContext2D(); + if (!context) { + return false; + } else { + if (context.setLineDash !== undefined) { + ol.has.CANVAS_LINE_DASH = true; + } + return true; + } + } catch (e) { + return false; + } + })(); + + +/** + * Indicates if DeviceOrientation is supported in the user's browser. + * @const + * @type {boolean} + * @api stable + */ +ol.has.DEVICE_ORIENTATION = 'DeviceOrientationEvent' in ol.global; + + +/** + * True if `ol.ENABLE_DOM` is set to `true` at compile time. + * @const + * @type {boolean} + */ +ol.has.DOM = ol.ENABLE_DOM; + + +/** + * Is HTML5 geolocation supported in the current browser? + * @const + * @type {boolean} + * @api stable + */ +ol.has.GEOLOCATION = 'geolocation' in ol.global.navigator; + + +/** + * True if browser supports touch events. + * @const + * @type {boolean} + * @api stable + */ +ol.has.TOUCH = ol.ASSUME_TOUCH || 'ontouchstart' in ol.global; + + +/** + * True if browser supports pointer events. + * @const + * @type {boolean} + */ +ol.has.POINTER = 'PointerEvent' in ol.global; + + +/** + * True if browser supports ms pointer events (IE 10). + * @const + * @type {boolean} + */ +ol.has.MSPOINTER = !!(ol.global.navigator.msPointerEnabled); + + +/** + * True if both OpenLayers and browser support WebGL. Always `false` + * if `ol.ENABLE_WEBGL` is set to `false` at compile time. + * @const + * @type {boolean} + * @api stable + */ +ol.has.WEBGL; + + +(function() { + if (ol.ENABLE_WEBGL) { + var hasWebGL = false; + var textureSize; + var /** @type {Array.<string>} */ extensions = []; + + if ('WebGLRenderingContext' in ol.global) { + try { + var canvas = /** @type {HTMLCanvasElement} */ + (document.createElement('CANVAS')); + var gl = ol.webgl.getContext(canvas, { + failIfMajorPerformanceCaveat: true + }); + if (gl) { + hasWebGL = true; + textureSize = /** @type {number} */ + (gl.getParameter(gl.MAX_TEXTURE_SIZE)); + extensions = gl.getSupportedExtensions(); + } + } catch (e) { + // pass + } + } + ol.has.WEBGL = hasWebGL; + ol.WEBGL_EXTENSIONS = extensions; + ol.WEBGL_MAX_TEXTURE_SIZE = textureSize; + } +})(); + +goog.provide('ol.pointer.EventSource'); + + +/** + * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. + * @param {!Object.<string, function(Event)>} mapping Event + * mapping. + * @constructor + */ +ol.pointer.EventSource = function(dispatcher, mapping) { + /** + * @type {ol.pointer.PointerEventHandler} + */ + this.dispatcher = dispatcher; + + /** + * @private + * @const + * @type {!Object.<string, function(Event)>} + */ + this.mapping_ = mapping; +}; + + +/** + * List of events supported by this source. + * @return {Array.<string>} Event names + */ +ol.pointer.EventSource.prototype.getEvents = function() { + return Object.keys(this.mapping_); +}; + + +/** + * Returns a mapping between the supported event types and + * the handlers that should handle an event. + * @return {Object.<string, function(Event)>} + * Event/Handler mapping + */ +ol.pointer.EventSource.prototype.getMapping = function() { + return this.mapping_; +}; + + +/** + * Returns the handler that should handle a given event type. + * @param {string} eventType The event type. + * @return {function(Event)} Handler + */ +ol.pointer.EventSource.prototype.getHandlerForEvent = function(eventType) { + return this.mapping_[eventType]; +}; + +// Based on https://github.com/Polymer/PointerEvents + +// Copyright (c) 2013 The Polymer Authors. All rights reserved. +// +// Redistribution and use 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +goog.provide('ol.pointer.MouseSource'); + +goog.require('ol.pointer.EventSource'); + + +/** + * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. + * @constructor + * @extends {ol.pointer.EventSource} + */ +ol.pointer.MouseSource = function(dispatcher) { + var mapping = { + 'mousedown': this.mousedown, + 'mousemove': this.mousemove, + 'mouseup': this.mouseup, + 'mouseover': this.mouseover, + 'mouseout': this.mouseout + }; + ol.pointer.EventSource.call(this, dispatcher, mapping); + + /** + * @const + * @type {!Object.<string, Event|Object>} + */ + this.pointerMap = dispatcher.pointerMap; + + /** + * @const + * @type {Array.<ol.Pixel>} + */ + this.lastTouches = []; +}; +ol.inherits(ol.pointer.MouseSource, ol.pointer.EventSource); + + +/** + * @const + * @type {number} + */ +ol.pointer.MouseSource.POINTER_ID = 1; + + +/** + * @const + * @type {string} + */ +ol.pointer.MouseSource.POINTER_TYPE = 'mouse'; + + +/** + * Radius around touchend that swallows mouse events. + * + * @const + * @type {number} + */ +ol.pointer.MouseSource.DEDUP_DIST = 25; + + +/** + * Detect if a mouse event was simulated from a touch by + * checking if previously there was a touch event at the + * same position. + * + * FIXME - Known problem with the native Android browser on + * Samsung GT-I9100 (Android 4.1.2): + * In case the page is scrolled, this function does not work + * correctly when a canvas is used (WebGL or canvas renderer). + * Mouse listeners on canvas elements (for this browser), create + * two mouse events: One 'good' and one 'bad' one (on other browsers or + * when a div is used, there is only one event). For the 'bad' one, + * clientX/clientY and also pageX/pageY are wrong when the page + * is scrolled. Because of that, this function can not detect if + * the events were simulated from a touch event. As result, a + * pointer event at a wrong position is dispatched, which confuses + * the map interactions. + * It is unclear, how one can get the correct position for the event + * or detect that the positions are invalid. + * + * @private + * @param {Event} inEvent The in event. + * @return {boolean} True, if the event was generated by a touch. + */ +ol.pointer.MouseSource.prototype.isEventSimulatedFromTouch_ = function(inEvent) { + var lts = this.lastTouches; + var x = inEvent.clientX, y = inEvent.clientY; + for (var i = 0, l = lts.length, t; i < l && (t = lts[i]); i++) { + // simulated mouse events will be swallowed near a primary touchend + var dx = Math.abs(x - t[0]), dy = Math.abs(y - t[1]); + if (dx <= ol.pointer.MouseSource.DEDUP_DIST && + dy <= ol.pointer.MouseSource.DEDUP_DIST) { + return true; + } + } + return false; +}; + + +/** + * Creates a copy of the original event that will be used + * for the fake pointer event. + * + * @param {Event} inEvent The in event. + * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. + * @return {Object} The copied event. + */ +ol.pointer.MouseSource.prepareEvent = function(inEvent, dispatcher) { + var e = dispatcher.cloneEvent(inEvent, inEvent); + + // forward mouse preventDefault + var pd = e.preventDefault; + e.preventDefault = function() { + inEvent.preventDefault(); + pd(); + }; + + e.pointerId = ol.pointer.MouseSource.POINTER_ID; + e.isPrimary = true; + e.pointerType = ol.pointer.MouseSource.POINTER_TYPE; + + return e; +}; + + +/** + * Handler for `mousedown`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MouseSource.prototype.mousedown = function(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + // TODO(dfreedman) workaround for some elements not sending mouseup + // http://crbug/149091 + if (ol.pointer.MouseSource.POINTER_ID.toString() in this.pointerMap) { + this.cancel(inEvent); + } + var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); + this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()] = inEvent; + this.dispatcher.down(e, inEvent); + } +}; + + +/** + * Handler for `mousemove`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MouseSource.prototype.mousemove = function(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); + this.dispatcher.move(e, inEvent); + } +}; + + +/** + * Handler for `mouseup`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MouseSource.prototype.mouseup = function(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + var p = this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()]; + + if (p && p.button === inEvent.button) { + var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); + this.dispatcher.up(e, inEvent); + this.cleanupMouse(); + } + } +}; + + +/** + * Handler for `mouseover`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MouseSource.prototype.mouseover = function(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); + this.dispatcher.enterOver(e, inEvent); + } +}; + + +/** + * Handler for `mouseout`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MouseSource.prototype.mouseout = function(inEvent) { + if (!this.isEventSimulatedFromTouch_(inEvent)) { + var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); + this.dispatcher.leaveOut(e, inEvent); + } +}; + + +/** + * Dispatches a `pointercancel` event. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MouseSource.prototype.cancel = function(inEvent) { + var e = ol.pointer.MouseSource.prepareEvent(inEvent, this.dispatcher); + this.dispatcher.cancel(e, inEvent); + this.cleanupMouse(); +}; + + +/** + * Remove the mouse from the list of active pointers. + */ +ol.pointer.MouseSource.prototype.cleanupMouse = function() { + delete this.pointerMap[ol.pointer.MouseSource.POINTER_ID.toString()]; +}; + +// Based on https://github.com/Polymer/PointerEvents + +// Copyright (c) 2013 The Polymer Authors. All rights reserved. +// +// Redistribution and use 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +goog.provide('ol.pointer.MsSource'); + +goog.require('ol.pointer.EventSource'); + + +/** + * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. + * @constructor + * @extends {ol.pointer.EventSource} + */ +ol.pointer.MsSource = function(dispatcher) { + var mapping = { + 'MSPointerDown': this.msPointerDown, + 'MSPointerMove': this.msPointerMove, + 'MSPointerUp': this.msPointerUp, + 'MSPointerOut': this.msPointerOut, + 'MSPointerOver': this.msPointerOver, + 'MSPointerCancel': this.msPointerCancel, + 'MSGotPointerCapture': this.msGotPointerCapture, + 'MSLostPointerCapture': this.msLostPointerCapture + }; + ol.pointer.EventSource.call(this, dispatcher, mapping); + + /** + * @const + * @type {!Object.<string, Event|Object>} + */ + this.pointerMap = dispatcher.pointerMap; + + /** + * @const + * @type {Array.<string>} + */ + this.POINTER_TYPES = [ + '', + 'unavailable', + 'touch', + 'pen', + 'mouse' + ]; +}; +ol.inherits(ol.pointer.MsSource, ol.pointer.EventSource); + + +/** + * Creates a copy of the original event that will be used + * for the fake pointer event. + * + * @private + * @param {Event} inEvent The in event. + * @return {Object} The copied event. + */ +ol.pointer.MsSource.prototype.prepareEvent_ = function(inEvent) { + var e = inEvent; + if (goog.isNumber(inEvent.pointerType)) { + e = this.dispatcher.cloneEvent(inEvent, inEvent); + e.pointerType = this.POINTER_TYPES[inEvent.pointerType]; + } + + return e; +}; + + +/** + * Remove this pointer from the list of active pointers. + * @param {number} pointerId Pointer identifier. + */ +ol.pointer.MsSource.prototype.cleanup = function(pointerId) { + delete this.pointerMap[pointerId.toString()]; +}; + + +/** + * Handler for `msPointerDown`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msPointerDown = function(inEvent) { + this.pointerMap[inEvent.pointerId.toString()] = inEvent; + var e = this.prepareEvent_(inEvent); + this.dispatcher.down(e, inEvent); +}; + + +/** + * Handler for `msPointerMove`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msPointerMove = function(inEvent) { + var e = this.prepareEvent_(inEvent); + this.dispatcher.move(e, inEvent); +}; + + +/** + * Handler for `msPointerUp`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msPointerUp = function(inEvent) { + var e = this.prepareEvent_(inEvent); + this.dispatcher.up(e, inEvent); + this.cleanup(inEvent.pointerId); +}; + + +/** + * Handler for `msPointerOut`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msPointerOut = function(inEvent) { + var e = this.prepareEvent_(inEvent); + this.dispatcher.leaveOut(e, inEvent); +}; + + +/** + * Handler for `msPointerOver`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msPointerOver = function(inEvent) { + var e = this.prepareEvent_(inEvent); + this.dispatcher.enterOver(e, inEvent); +}; + + +/** + * Handler for `msPointerCancel`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msPointerCancel = function(inEvent) { + var e = this.prepareEvent_(inEvent); + this.dispatcher.cancel(e, inEvent); + this.cleanup(inEvent.pointerId); +}; + + +/** + * Handler for `msLostPointerCapture`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msLostPointerCapture = function(inEvent) { + var e = this.dispatcher.makeEvent('lostpointercapture', + inEvent, inEvent); + this.dispatcher.dispatchEvent(e); +}; + + +/** + * Handler for `msGotPointerCapture`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.MsSource.prototype.msGotPointerCapture = function(inEvent) { + var e = this.dispatcher.makeEvent('gotpointercapture', + inEvent, inEvent); + this.dispatcher.dispatchEvent(e); +}; + +// Based on https://github.com/Polymer/PointerEvents + +// Copyright (c) 2013 The Polymer Authors. All rights reserved. +// +// Redistribution and use 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +goog.provide('ol.pointer.NativeSource'); + +goog.require('ol.pointer.EventSource'); + + +/** + * @param {ol.pointer.PointerEventHandler} dispatcher Event handler. + * @constructor + * @extends {ol.pointer.EventSource} + */ +ol.pointer.NativeSource = function(dispatcher) { + var mapping = { + 'pointerdown': this.pointerDown, + 'pointermove': this.pointerMove, + 'pointerup': this.pointerUp, + 'pointerout': this.pointerOut, + 'pointerover': this.pointerOver, + 'pointercancel': this.pointerCancel, + 'gotpointercapture': this.gotPointerCapture, + 'lostpointercapture': this.lostPointerCapture + }; + ol.pointer.EventSource.call(this, dispatcher, mapping); +}; +ol.inherits(ol.pointer.NativeSource, ol.pointer.EventSource); + + +/** + * Handler for `pointerdown`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.pointerDown = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + + +/** + * Handler for `pointermove`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.pointerMove = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + + +/** + * Handler for `pointerup`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.pointerUp = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + + +/** + * Handler for `pointerout`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.pointerOut = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + + +/** + * Handler for `pointerover`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.pointerOver = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + + +/** + * Handler for `pointercancel`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.pointerCancel = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + + +/** + * Handler for `lostpointercapture`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.lostPointerCapture = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + + +/** + * Handler for `gotpointercapture`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.NativeSource.prototype.gotPointerCapture = function(inEvent) { + this.dispatcher.fireNativeEvent(inEvent); +}; + +// Based on https://github.com/Polymer/PointerEvents + +// Copyright (c) 2013 The Polymer Authors. All rights reserved. +// +// Redistribution and use 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +goog.provide('ol.pointer.TouchSource'); + +goog.require('ol'); +goog.require('ol.array'); +goog.require('ol.pointer.EventSource'); +goog.require('ol.pointer.MouseSource'); + + +/** + * @constructor + * @param {ol.pointer.PointerEventHandler} dispatcher The event handler. + * @param {ol.pointer.MouseSource} mouseSource Mouse source. + * @extends {ol.pointer.EventSource} + */ +ol.pointer.TouchSource = function(dispatcher, mouseSource) { + var mapping = { + 'touchstart': this.touchstart, + 'touchmove': this.touchmove, + 'touchend': this.touchend, + 'touchcancel': this.touchcancel + }; + ol.pointer.EventSource.call(this, dispatcher, mapping); + + /** + * @const + * @type {!Object.<string, Event|Object>} + */ + this.pointerMap = dispatcher.pointerMap; + + /** + * @const + * @type {ol.pointer.MouseSource} + */ + this.mouseSource = mouseSource; + + /** + * @private + * @type {number|undefined} + */ + this.firstTouchId_ = undefined; + + /** + * @private + * @type {number} + */ + this.clickCount_ = 0; + + /** + * @private + * @type {number|undefined} + */ + this.resetId_ = undefined; +}; +ol.inherits(ol.pointer.TouchSource, ol.pointer.EventSource); + + +/** + * Mouse event timeout: This should be long enough to + * ignore compat mouse events made by touch. + * @const + * @type {number} + */ +ol.pointer.TouchSource.DEDUP_TIMEOUT = 2500; + + +/** + * @const + * @type {number} + */ +ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT = 200; + + +/** + * @const + * @type {string} + */ +ol.pointer.TouchSource.POINTER_TYPE = 'touch'; + + +/** + * @private + * @param {Touch} inTouch The in touch. + * @return {boolean} True, if this is the primary touch. + */ +ol.pointer.TouchSource.prototype.isPrimaryTouch_ = function(inTouch) { + return this.firstTouchId_ === inTouch.identifier; +}; + + +/** + * Set primary touch if there are no pointers, or the only pointer is the mouse. + * @param {Touch} inTouch The in touch. + * @private + */ +ol.pointer.TouchSource.prototype.setPrimaryTouch_ = function(inTouch) { + var count = Object.keys(this.pointerMap).length; + if (count === 0 || (count === 1 && + ol.pointer.MouseSource.POINTER_ID.toString() in this.pointerMap)) { + this.firstTouchId_ = inTouch.identifier; + this.cancelResetClickCount_(); + } +}; + + +/** + * @private + * @param {Object} inPointer The in pointer object. + */ +ol.pointer.TouchSource.prototype.removePrimaryPointer_ = function(inPointer) { + if (inPointer.isPrimary) { + this.firstTouchId_ = undefined; + this.resetClickCount_(); + } +}; + + +/** + * @private + */ +ol.pointer.TouchSource.prototype.resetClickCount_ = function() { + this.resetId_ = ol.global.setTimeout( + this.resetClickCountHandler_.bind(this), + ol.pointer.TouchSource.CLICK_COUNT_TIMEOUT); +}; + + +/** + * @private + */ +ol.pointer.TouchSource.prototype.resetClickCountHandler_ = function() { + this.clickCount_ = 0; + this.resetId_ = undefined; +}; + + +/** + * @private + */ +ol.pointer.TouchSource.prototype.cancelResetClickCount_ = function() { + if (this.resetId_ !== undefined) { + ol.global.clearTimeout(this.resetId_); + } +}; + + +/** + * @private + * @param {Event} browserEvent Browser event + * @param {Touch} inTouch Touch event + * @return {Object} A pointer object. + */ +ol.pointer.TouchSource.prototype.touchToPointer_ = function(browserEvent, inTouch) { + var e = this.dispatcher.cloneEvent(browserEvent, inTouch); + // Spec specifies that pointerId 1 is reserved for Mouse. + // Touch identifiers can start at 0. + // Add 2 to the touch identifier for compatibility. + e.pointerId = inTouch.identifier + 2; + // TODO: check if this is necessary? + //e.target = findTarget(e); + e.bubbles = true; + e.cancelable = true; + e.detail = this.clickCount_; + e.button = 0; + e.buttons = 1; + e.width = inTouch.webkitRadiusX || inTouch.radiusX || 0; + e.height = inTouch.webkitRadiusY || inTouch.radiusY || 0; + e.pressure = inTouch.webkitForce || inTouch.force || 0.5; + e.isPrimary = this.isPrimaryTouch_(inTouch); + e.pointerType = ol.pointer.TouchSource.POINTER_TYPE; + + // make sure that the properties that are different for + // each `Touch` object are not copied from the BrowserEvent object + e.clientX = inTouch.clientX; + e.clientY = inTouch.clientY; + e.screenX = inTouch.screenX; + e.screenY = inTouch.screenY; + + return e; +}; + + +/** + * @private + * @param {Event} inEvent Touch event + * @param {function(Event, Object)} inFunction In function. + */ +ol.pointer.TouchSource.prototype.processTouches_ = function(inEvent, inFunction) { + var touches = Array.prototype.slice.call( + inEvent.changedTouches); + var count = touches.length; + function preventDefault() { + inEvent.preventDefault(); + } + var i, pointer; + for (i = 0; i < count; ++i) { + pointer = this.touchToPointer_(inEvent, touches[i]); + // forward touch preventDefaults + pointer.preventDefault = preventDefault; + inFunction.call(this, inEvent, pointer); + } +}; + + +/** + * @private + * @param {TouchList} touchList The touch list. + * @param {number} searchId Search identifier. + * @return {boolean} True, if the `Touch` with the given id is in the list. + */ +ol.pointer.TouchSource.prototype.findTouch_ = function(touchList, searchId) { + var l = touchList.length; + var touch; + for (var i = 0; i < l; i++) { + touch = touchList[i]; + if (touch.identifier === searchId) { + return true; + } + } + return false; +}; + + +/** + * In some instances, a touchstart can happen without a touchend. This + * leaves the pointermap in a broken state. + * Therefore, on every touchstart, we remove the touches that did not fire a + * touchend event. + * To keep state globally consistent, we fire a pointercancel for + * this "abandoned" touch + * + * @private + * @param {Event} inEvent The in event. + */ +ol.pointer.TouchSource.prototype.vacuumTouches_ = function(inEvent) { + var touchList = inEvent.touches; + // pointerMap.getCount() should be < touchList.length here, + // as the touchstart has not been processed yet. + var keys = Object.keys(this.pointerMap); + var count = keys.length; + if (count >= touchList.length) { + var d = []; + var i, key, value; + for (i = 0; i < count; ++i) { + key = keys[i]; + value = this.pointerMap[key]; + // Never remove pointerId == 1, which is mouse. + // Touch identifiers are 2 smaller than their pointerId, which is the + // index in pointermap. + if (key != ol.pointer.MouseSource.POINTER_ID && + !this.findTouch_(touchList, key - 2)) { + d.push(value.out); + } + } + for (i = 0; i < d.length; ++i) { + this.cancelOut_(inEvent, d[i]); + } + } +}; + + +/** + * Handler for `touchstart`, triggers `pointerover`, + * `pointerenter` and `pointerdown` events. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.TouchSource.prototype.touchstart = function(inEvent) { + this.vacuumTouches_(inEvent); + this.setPrimaryTouch_(inEvent.changedTouches[0]); + this.dedupSynthMouse_(inEvent); + this.clickCount_++; + this.processTouches_(inEvent, this.overDown_); +}; + + +/** + * @private + * @param {Event} browserEvent The event. + * @param {Object} inPointer The in pointer object. + */ +ol.pointer.TouchSource.prototype.overDown_ = function(browserEvent, inPointer) { + this.pointerMap[inPointer.pointerId] = { + target: inPointer.target, + out: inPointer, + outTarget: inPointer.target + }; + this.dispatcher.over(inPointer, browserEvent); + this.dispatcher.enter(inPointer, browserEvent); + this.dispatcher.down(inPointer, browserEvent); +}; + + +/** + * Handler for `touchmove`. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.TouchSource.prototype.touchmove = function(inEvent) { + inEvent.preventDefault(); + this.processTouches_(inEvent, this.moveOverOut_); +}; + + +/** + * @private + * @param {Event} browserEvent The event. + * @param {Object} inPointer The in pointer. + */ +ol.pointer.TouchSource.prototype.moveOverOut_ = function(browserEvent, inPointer) { + var event = inPointer; + var pointer = this.pointerMap[event.pointerId]; + // a finger drifted off the screen, ignore it + if (!pointer) { + return; + } + var outEvent = pointer.out; + var outTarget = pointer.outTarget; + this.dispatcher.move(event, browserEvent); + if (outEvent && outTarget !== event.target) { + outEvent.relatedTarget = event.target; + event.relatedTarget = outTarget; + // recover from retargeting by shadow + outEvent.target = outTarget; + if (event.target) { + this.dispatcher.leaveOut(outEvent, browserEvent); + this.dispatcher.enterOver(event, browserEvent); + } else { + // clean up case when finger leaves the screen + event.target = outTarget; + event.relatedTarget = null; + this.cancelOut_(browserEvent, event); + } + } + pointer.out = event; + pointer.outTarget = event.target; +}; + + +/** + * Handler for `touchend`, triggers `pointerup`, + * `pointerout` and `pointerleave` events. + * + * @param {Event} inEvent The event. + */ +ol.pointer.TouchSource.prototype.touchend = function(inEvent) { + this.dedupSynthMouse_(inEvent); + this.processTouches_(inEvent, this.upOut_); +}; + + +/** + * @private + * @param {Event} browserEvent An event. + * @param {Object} inPointer The inPointer object. + */ +ol.pointer.TouchSource.prototype.upOut_ = function(browserEvent, inPointer) { + this.dispatcher.up(inPointer, browserEvent); + this.dispatcher.out(inPointer, browserEvent); + this.dispatcher.leave(inPointer, browserEvent); + this.cleanUpPointer_(inPointer); +}; + + +/** + * Handler for `touchcancel`, triggers `pointercancel`, + * `pointerout` and `pointerleave` events. + * + * @param {Event} inEvent The in event. + */ +ol.pointer.TouchSource.prototype.touchcancel = function(inEvent) { + this.processTouches_(inEvent, this.cancelOut_); +}; + + +/** + * @private + * @param {Event} browserEvent The event. + * @param {Object} inPointer The in pointer. + */ +ol.pointer.TouchSource.prototype.cancelOut_ = function(browserEvent, inPointer) { + this.dispatcher.cancel(inPointer, browserEvent); + this.dispatcher.out(inPointer, browserEvent); + this.dispatcher.leave(inPointer, browserEvent); + this.cleanUpPointer_(inPointer); +}; + + +/** + * @private + * @param {Object} inPointer The inPointer object. + */ +ol.pointer.TouchSource.prototype.cleanUpPointer_ = function(inPointer) { + delete this.pointerMap[inPointer.pointerId]; + this.removePrimaryPointer_(inPointer); +}; + + +/** + * Prevent synth mouse events from creating pointer events. + * + * @private + * @param {Event} inEvent The in event. + */ +ol.pointer.TouchSource.prototype.dedupSynthMouse_ = function(inEvent) { + var lts = this.mouseSource.lastTouches; + var t = inEvent.changedTouches[0]; + // only the primary finger will synth mouse events + if (this.isPrimaryTouch_(t)) { + // remember x/y of last touch + var lt = [t.clientX, t.clientY]; + lts.push(lt); + + ol.global.setTimeout(function() { + // remove touch after timeout + ol.array.remove(lts, lt); + }, ol.pointer.TouchSource.DEDUP_TIMEOUT); + } +}; + +// Based on https://github.com/Polymer/PointerEvents + +// Copyright (c) 2013 The Polymer Authors. All rights reserved. +// +// Redistribution and use 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 Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// 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. + +goog.provide('ol.pointer.PointerEventHandler'); + +goog.require('ol.events'); +goog.require('ol.events.EventTarget'); + +goog.require('ol.has'); +goog.require('ol.pointer.MouseSource'); +goog.require('ol.pointer.MsSource'); +goog.require('ol.pointer.NativeSource'); +goog.require('ol.pointer.PointerEvent'); +goog.require('ol.pointer.TouchSource'); + + +/** + * @constructor + * @extends {ol.events.EventTarget} + * @param {Element|HTMLDocument} element Viewport element. + */ +ol.pointer.PointerEventHandler = function(element) { + ol.events.EventTarget.call(this); + + /** + * @const + * @private + * @type {Element|HTMLDocument} + */ + this.element_ = element; + + /** + * @const + * @type {!Object.<string, Event|Object>} + */ + this.pointerMap = {}; + + /** + * @type {Object.<string, function(Event)>} + * @private + */ + this.eventMap_ = {}; + + /** + * @type {Array.<ol.pointer.EventSource>} + * @private + */ + this.eventSourceList_ = []; + + this.registerSources(); +}; +ol.inherits(ol.pointer.PointerEventHandler, ol.events.EventTarget); + + +/** + * Set up the event sources (mouse, touch and native pointers) + * that generate pointer events. + */ +ol.pointer.PointerEventHandler.prototype.registerSources = function() { + if (ol.has.POINTER) { + this.registerSource('native', new ol.pointer.NativeSource(this)); + } else if (ol.has.MSPOINTER) { + this.registerSource('ms', new ol.pointer.MsSource(this)); + } else { + var mouseSource = new ol.pointer.MouseSource(this); + this.registerSource('mouse', mouseSource); + + if (ol.has.TOUCH) { + this.registerSource('touch', + new ol.pointer.TouchSource(this, mouseSource)); + } + } + + // register events on the viewport element + this.register_(); +}; + + +/** + * Add a new event source that will generate pointer events. + * + * @param {string} name A name for the event source + * @param {ol.pointer.EventSource} source The source event. + */ +ol.pointer.PointerEventHandler.prototype.registerSource = function(name, source) { + var s = source; + var newEvents = s.getEvents(); + + if (newEvents) { + newEvents.forEach(function(e) { + var handler = s.getHandlerForEvent(e); + + if (handler) { + this.eventMap_[e] = handler.bind(s); + } + }, this); + this.eventSourceList_.push(s); + } +}; + + +/** + * Set up the events for all registered event sources. + * @private + */ +ol.pointer.PointerEventHandler.prototype.register_ = function() { + var l = this.eventSourceList_.length; + var eventSource; + for (var i = 0; i < l; i++) { + eventSource = this.eventSourceList_[i]; + this.addEvents_(eventSource.getEvents()); + } +}; + + +/** + * Remove all registered events. + * @private + */ +ol.pointer.PointerEventHandler.prototype.unregister_ = function() { + var l = this.eventSourceList_.length; + var eventSource; + for (var i = 0; i < l; i++) { + eventSource = this.eventSourceList_[i]; + this.removeEvents_(eventSource.getEvents()); + } +}; + + +/** + * Calls the right handler for a new event. + * @private + * @param {Event} inEvent Browser event. + */ +ol.pointer.PointerEventHandler.prototype.eventHandler_ = function(inEvent) { + var type = inEvent.type; + var handler = this.eventMap_[type]; + if (handler) { + handler(inEvent); + } +}; + + +/** + * Setup listeners for the given events. + * @private + * @param {Array.<string>} events List of events. + */ +ol.pointer.PointerEventHandler.prototype.addEvents_ = function(events) { + events.forEach(function(eventName) { + ol.events.listen(this.element_, eventName, this.eventHandler_, this); + }, this); +}; + + +/** + * Unregister listeners for the given events. + * @private + * @param {Array.<string>} events List of events. + */ +ol.pointer.PointerEventHandler.prototype.removeEvents_ = function(events) { + events.forEach(function(e) { + ol.events.unlisten(this.element_, e, this.eventHandler_, this); + }, this); +}; + + +/** + * Returns a snapshot of inEvent, with writable properties. + * + * @param {Event} event Browser event. + * @param {Event|Touch} inEvent An event that contains + * properties to copy. + * @return {Object} An object containing shallow copies of + * `inEvent`'s properties. + */ +ol.pointer.PointerEventHandler.prototype.cloneEvent = function(event, inEvent) { + var eventCopy = {}, p; + for (var i = 0, ii = ol.pointer.CLONE_PROPS.length; i < ii; i++) { + p = ol.pointer.CLONE_PROPS[i][0]; + eventCopy[p] = event[p] || inEvent[p] || ol.pointer.CLONE_PROPS[i][1]; + } + + return eventCopy; +}; + + +// EVENTS + + +/** + * Triggers a 'pointerdown' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.down = function(data, event) { + this.fireEvent(ol.pointer.EventType.POINTERDOWN, data, event); +}; + + +/** + * Triggers a 'pointermove' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.move = function(data, event) { + this.fireEvent(ol.pointer.EventType.POINTERMOVE, data, event); +}; + + +/** + * Triggers a 'pointerup' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.up = function(data, event) { + this.fireEvent(ol.pointer.EventType.POINTERUP, data, event); +}; + + +/** + * Triggers a 'pointerenter' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.enter = function(data, event) { + data.bubbles = false; + this.fireEvent(ol.pointer.EventType.POINTERENTER, data, event); +}; + + +/** + * Triggers a 'pointerleave' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.leave = function(data, event) { + data.bubbles = false; + this.fireEvent(ol.pointer.EventType.POINTERLEAVE, data, event); +}; + + +/** + * Triggers a 'pointerover' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.over = function(data, event) { + data.bubbles = true; + this.fireEvent(ol.pointer.EventType.POINTEROVER, data, event); +}; + + +/** + * Triggers a 'pointerout' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.out = function(data, event) { + data.bubbles = true; + this.fireEvent(ol.pointer.EventType.POINTEROUT, data, event); +}; + + +/** + * Triggers a 'pointercancel' event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.cancel = function(data, event) { + this.fireEvent(ol.pointer.EventType.POINTERCANCEL, data, event); +}; + + +/** + * Triggers a combination of 'pointerout' and 'pointerleave' events. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.leaveOut = function(data, event) { + this.out(data, event); + if (!this.contains_(data.target, data.relatedTarget)) { + this.leave(data, event); + } +}; + + +/** + * Triggers a combination of 'pointerover' and 'pointerevents' events. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.enterOver = function(data, event) { + this.over(data, event); + if (!this.contains_(data.target, data.relatedTarget)) { + this.enter(data, event); + } +}; + + +/** + * @private + * @param {Element} container The container element. + * @param {Element} contained The contained element. + * @return {boolean} Returns true if the container element + * contains the other element. + */ +ol.pointer.PointerEventHandler.prototype.contains_ = function(container, contained) { + if (!container || !contained) { + return false; + } + return container.contains(contained); +}; + + +// EVENT CREATION AND TRACKING +/** + * Creates a new Event of type `inType`, based on the information in + * `data`. + * + * @param {string} inType A string representing the type of event to create. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + * @return {ol.pointer.PointerEvent} A PointerEvent of type `inType`. + */ +ol.pointer.PointerEventHandler.prototype.makeEvent = function(inType, data, event) { + return new ol.pointer.PointerEvent(inType, event, data); +}; + + +/** + * Make and dispatch an event in one call. + * @param {string} inType A string representing the type of event. + * @param {Object} data Pointer event data. + * @param {Event} event The event. + */ +ol.pointer.PointerEventHandler.prototype.fireEvent = function(inType, data, event) { + var e = this.makeEvent(inType, data, event); + this.dispatchEvent(e); +}; + + +/** + * Creates a pointer event from a native pointer event + * and dispatches this event. + * @param {Event} event A platform event with a target. + */ +ol.pointer.PointerEventHandler.prototype.fireNativeEvent = function(event) { + var e = this.makeEvent(event.type, event, event); + this.dispatchEvent(e); +}; + + +/** + * Wrap a native mouse event into a pointer event. + * This proxy method is required for the legacy IE support. + * @param {string} eventType The pointer event type. + * @param {Event} event The event. + * @return {ol.pointer.PointerEvent} The wrapped event. + */ +ol.pointer.PointerEventHandler.prototype.wrapMouseEvent = function(eventType, event) { + var pointerEvent = this.makeEvent( + eventType, ol.pointer.MouseSource.prepareEvent(event, this), event); + return pointerEvent; +}; + + +/** + * @inheritDoc + */ +ol.pointer.PointerEventHandler.prototype.disposeInternal = function() { + this.unregister_(); + ol.events.EventTarget.prototype.disposeInternal.call(this); +}; + + +/** + * Constants for event names. + * @enum {string} + */ +ol.pointer.EventType = { + POINTERMOVE: 'pointermove', + POINTERDOWN: 'pointerdown', + POINTERUP: 'pointerup', + POINTEROVER: 'pointerover', + POINTEROUT: 'pointerout', + POINTERENTER: 'pointerenter', + POINTERLEAVE: 'pointerleave', + POINTERCANCEL: 'pointercancel' +}; + + +/** + * Properties to copy when cloning an event, with default values. + * @type {Array.<Array>} + */ +ol.pointer.CLONE_PROPS = [ + // MouseEvent + ['bubbles', false], + ['cancelable', false], + ['view', null], + ['detail', null], + ['screenX', 0], + ['screenY', 0], + ['clientX', 0], + ['clientY', 0], + ['ctrlKey', false], + ['altKey', false], + ['shiftKey', false], + ['metaKey', false], + ['button', 0], + ['relatedTarget', null], + // DOM Level 3 + ['buttons', 0], + // PointerEvent + ['pointerId', 0], + ['width', 0], + ['height', 0], + ['pressure', 0], + ['tiltX', 0], + ['tiltY', 0], + ['pointerType', ''], + ['hwTimestamp', 0], + ['isPrimary', false], + // event instance + ['type', ''], + ['target', null], + ['currentTarget', null], + ['which', 0] +]; + +goog.provide('ol.MapBrowserEvent'); +goog.provide('ol.MapBrowserEvent.EventType'); +goog.provide('ol.MapBrowserEventHandler'); +goog.provide('ol.MapBrowserPointerEvent'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.MapEvent'); +goog.require('ol.events'); +goog.require('ol.events.EventTarget'); +goog.require('ol.events.EventType'); +goog.require('ol.pointer.PointerEvent'); +goog.require('ol.pointer.PointerEventHandler'); + + +/** + * @classdesc + * Events emitted as map browser events are instances of this type. + * See {@link ol.Map} for which events trigger a map browser event. + * + * @constructor + * @extends {ol.MapEvent} + * @implements {oli.MapBrowserEvent} + * @param {string} type Event type. + * @param {ol.Map} map Map. + * @param {Event} browserEvent Browser event. + * @param {boolean=} opt_dragging Is the map currently being dragged? + * @param {?olx.FrameState=} opt_frameState Frame state. + */ +ol.MapBrowserEvent = function(type, map, browserEvent, opt_dragging, + opt_frameState) { + + ol.MapEvent.call(this, type, map, opt_frameState); + + /** + * The original browser event. + * @const + * @type {Event} + * @api stable + */ + this.originalEvent = browserEvent; + + /** + * The pixel of the original browser event. + * @type {ol.Pixel} + * @api stable + */ + this.pixel = map.getEventPixel(browserEvent); + + /** + * The coordinate of the original browser event. + * @type {ol.Coordinate} + * @api stable + */ + this.coordinate = map.getCoordinateFromPixel(this.pixel); + + /** + * Indicates if the map is currently being dragged. Only set for + * `POINTERDRAG` and `POINTERMOVE` events. Default is `false`. + * + * @type {boolean} + * @api stable + */ + this.dragging = opt_dragging !== undefined ? opt_dragging : false; + +}; +ol.inherits(ol.MapBrowserEvent, ol.MapEvent); + + +/** + * Prevents the default browser action. + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.preventDefault + * @override + * @api stable + */ +ol.MapBrowserEvent.prototype.preventDefault = function() { + ol.MapEvent.prototype.preventDefault.call(this); + this.originalEvent.preventDefault(); +}; + + +/** + * Prevents further propagation of the current event. + * @see https://developer.mozilla.org/en-US/docs/Web/API/event.stopPropagation + * @override + * @api stable + */ +ol.MapBrowserEvent.prototype.stopPropagation = function() { + ol.MapEvent.prototype.stopPropagation.call(this); + this.originalEvent.stopPropagation(); +}; + + +/** + * @constructor + * @extends {ol.MapBrowserEvent} + * @param {string} type Event type. + * @param {ol.Map} map Map. + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @param {boolean=} opt_dragging Is the map currently being dragged? + * @param {?olx.FrameState=} opt_frameState Frame state. + */ +ol.MapBrowserPointerEvent = function(type, map, pointerEvent, opt_dragging, + opt_frameState) { + + ol.MapBrowserEvent.call(this, type, map, pointerEvent.originalEvent, opt_dragging, + opt_frameState); + + /** + * @const + * @type {ol.pointer.PointerEvent} + */ + this.pointerEvent = pointerEvent; + +}; +ol.inherits(ol.MapBrowserPointerEvent, ol.MapBrowserEvent); + + +/** + * @param {ol.Map} map The map with the viewport to listen to events on. + * @constructor + * @extends {ol.events.EventTarget} + */ +ol.MapBrowserEventHandler = function(map) { + + ol.events.EventTarget.call(this); + + /** + * This is the element that we will listen to the real events on. + * @type {ol.Map} + * @private + */ + this.map_ = map; + + /** + * @type {number} + * @private + */ + this.clickTimeoutId_ = 0; + + /** + * @type {boolean} + * @private + */ + this.dragging_ = false; + + /** + * @type {!Array.<ol.EventsKey>} + * @private + */ + this.dragListenerKeys_ = []; + + /** + * The most recent "down" type event (or null if none have occurred). + * Set on pointerdown. + * @type {ol.pointer.PointerEvent} + * @private + */ + this.down_ = null; + + var element = this.map_.getViewport(); + + /** + * @type {number} + * @private + */ + this.activePointers_ = 0; + + /** + * @type {!Object.<number, boolean>} + * @private + */ + this.trackedTouches_ = {}; + + /** + * Event handler which generates pointer events for + * the viewport element. + * + * @type {ol.pointer.PointerEventHandler} + * @private + */ + this.pointerEventHandler_ = new ol.pointer.PointerEventHandler(element); + + /** + * Event handler which generates pointer events for + * the document (used when dragging). + * + * @type {ol.pointer.PointerEventHandler} + * @private + */ + this.documentPointerEventHandler_ = null; + + /** + * @type {?ol.EventsKey} + * @private + */ + this.pointerdownListenerKey_ = ol.events.listen(this.pointerEventHandler_, + ol.pointer.EventType.POINTERDOWN, + this.handlePointerDown_, this); + + /** + * @type {?ol.EventsKey} + * @private + */ + this.relayedListenerKey_ = ol.events.listen(this.pointerEventHandler_, + ol.pointer.EventType.POINTERMOVE, + this.relayEvent_, this); + +}; +ol.inherits(ol.MapBrowserEventHandler, ol.events.EventTarget); + + +/** + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @private + */ +ol.MapBrowserEventHandler.prototype.emulateClick_ = function(pointerEvent) { + var newEvent; + newEvent = new ol.MapBrowserPointerEvent( + ol.MapBrowserEvent.EventType.CLICK, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + if (this.clickTimeoutId_ !== 0) { + // double-click + ol.global.clearTimeout(this.clickTimeoutId_); + this.clickTimeoutId_ = 0; + newEvent = new ol.MapBrowserPointerEvent( + ol.MapBrowserEvent.EventType.DBLCLICK, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + } else { + // click + this.clickTimeoutId_ = ol.global.setTimeout(function() { + this.clickTimeoutId_ = 0; + var newEvent = new ol.MapBrowserPointerEvent( + ol.MapBrowserEvent.EventType.SINGLECLICK, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + }.bind(this), 250); + } +}; + + +/** + * Keeps track on how many pointers are currently active. + * + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @private + */ +ol.MapBrowserEventHandler.prototype.updateActivePointers_ = function(pointerEvent) { + var event = pointerEvent; + + if (event.type == ol.MapBrowserEvent.EventType.POINTERUP || + event.type == ol.MapBrowserEvent.EventType.POINTERCANCEL) { + delete this.trackedTouches_[event.pointerId]; + } else if (event.type == ol.MapBrowserEvent.EventType.POINTERDOWN) { + this.trackedTouches_[event.pointerId] = true; + } + this.activePointers_ = Object.keys(this.trackedTouches_).length; +}; + + +/** + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @private + */ +ol.MapBrowserEventHandler.prototype.handlePointerUp_ = function(pointerEvent) { + this.updateActivePointers_(pointerEvent); + var newEvent = new ol.MapBrowserPointerEvent( + ol.MapBrowserEvent.EventType.POINTERUP, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + + // We emulate click events on left mouse button click, touch contact, and pen + // contact. isMouseActionButton returns true in these cases (evt.button is set + // to 0). + // See http://www.w3.org/TR/pointerevents/#button-states + if (!this.dragging_ && this.isMouseActionButton_(pointerEvent)) { + goog.asserts.assert(this.down_, 'this.down_ must be truthy'); + this.emulateClick_(this.down_); + } + + goog.asserts.assert(this.activePointers_ >= 0, + 'this.activePointers_ should be equal to or larger than 0'); + if (this.activePointers_ === 0) { + this.dragListenerKeys_.forEach(ol.events.unlistenByKey); + this.dragListenerKeys_.length = 0; + this.dragging_ = false; + this.down_ = null; + this.documentPointerEventHandler_.dispose(); + this.documentPointerEventHandler_ = null; + } +}; + + +/** + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @return {boolean} If the left mouse button was pressed. + * @private + */ +ol.MapBrowserEventHandler.prototype.isMouseActionButton_ = function(pointerEvent) { + return pointerEvent.button === 0; +}; + + +/** + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @private + */ +ol.MapBrowserEventHandler.prototype.handlePointerDown_ = function(pointerEvent) { + this.updateActivePointers_(pointerEvent); + var newEvent = new ol.MapBrowserPointerEvent( + ol.MapBrowserEvent.EventType.POINTERDOWN, this.map_, pointerEvent); + this.dispatchEvent(newEvent); + + this.down_ = pointerEvent; + + if (this.dragListenerKeys_.length === 0) { + /* Set up a pointer event handler on the `document`, + * which is required when the pointer is moved outside + * the viewport when dragging. + */ + this.documentPointerEventHandler_ = + new ol.pointer.PointerEventHandler(document); + + this.dragListenerKeys_.push( + ol.events.listen(this.documentPointerEventHandler_, + ol.MapBrowserEvent.EventType.POINTERMOVE, + this.handlePointerMove_, this), + ol.events.listen(this.documentPointerEventHandler_, + ol.MapBrowserEvent.EventType.POINTERUP, + this.handlePointerUp_, this), + /* Note that the listener for `pointercancel is set up on + * `pointerEventHandler_` and not `documentPointerEventHandler_` like + * the `pointerup` and `pointermove` listeners. + * + * The reason for this is the following: `TouchSource.vacuumTouches_()` + * issues `pointercancel` events, when there was no `touchend` for a + * `touchstart`. Now, let's say a first `touchstart` is registered on + * `pointerEventHandler_`. The `documentPointerEventHandler_` is set up. + * But `documentPointerEventHandler_` doesn't know about the first + * `touchstart`. If there is no `touchend` for the `touchstart`, we can + * only receive a `touchcancel` from `pointerEventHandler_`, because it is + * only registered there. + */ + ol.events.listen(this.pointerEventHandler_, + ol.MapBrowserEvent.EventType.POINTERCANCEL, + this.handlePointerUp_, this) + ); + } +}; + + +/** + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @private + */ +ol.MapBrowserEventHandler.prototype.handlePointerMove_ = function(pointerEvent) { + // Fix IE10 on windows Surface : When you tap the tablet, it triggers + // multiple pointermove events between pointerdown and pointerup with + // the exact same coordinates of the pointerdown event. To avoid a + // 'false' touchmove event to be dispatched , we test if the pointer + // effectively moved. + if (this.isMoving_(pointerEvent)) { + this.dragging_ = true; + var newEvent = new ol.MapBrowserPointerEvent( + ol.MapBrowserEvent.EventType.POINTERDRAG, this.map_, pointerEvent, + this.dragging_); + this.dispatchEvent(newEvent); + } + + // Some native android browser triggers mousemove events during small period + // of time. See: https://code.google.com/p/android/issues/detail?id=5491 or + // https://code.google.com/p/android/issues/detail?id=19827 + // ex: Galaxy Tab P3110 + Android 4.1.1 + pointerEvent.preventDefault(); +}; + + +/** + * Wrap and relay a pointer event. Note that this requires that the type + * string for the MapBrowserPointerEvent matches the PointerEvent type. + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @private + */ +ol.MapBrowserEventHandler.prototype.relayEvent_ = function(pointerEvent) { + var dragging = !!(this.down_ && this.isMoving_(pointerEvent)); + this.dispatchEvent(new ol.MapBrowserPointerEvent( + pointerEvent.type, this.map_, pointerEvent, dragging)); +}; + + +/** + * @param {ol.pointer.PointerEvent} pointerEvent Pointer event. + * @return {boolean} Is moving. + * @private + */ +ol.MapBrowserEventHandler.prototype.isMoving_ = function(pointerEvent) { + return pointerEvent.clientX != this.down_.clientX || + pointerEvent.clientY != this.down_.clientY; +}; + + +/** + * @inheritDoc + */ +ol.MapBrowserEventHandler.prototype.disposeInternal = function() { + if (this.relayedListenerKey_) { + ol.events.unlistenByKey(this.relayedListenerKey_); + this.relayedListenerKey_ = null; + } + if (this.pointerdownListenerKey_) { + ol.events.unlistenByKey(this.pointerdownListenerKey_); + this.pointerdownListenerKey_ = null; + } + + this.dragListenerKeys_.forEach(ol.events.unlistenByKey); + this.dragListenerKeys_.length = 0; + + if (this.documentPointerEventHandler_) { + this.documentPointerEventHandler_.dispose(); + this.documentPointerEventHandler_ = null; + } + if (this.pointerEventHandler_) { + this.pointerEventHandler_.dispose(); + this.pointerEventHandler_ = null; + } + ol.events.EventTarget.prototype.disposeInternal.call(this); +}; + + +/** + * Constants for event names. + * @enum {string} + */ +ol.MapBrowserEvent.EventType = { + + /** + * A true single click with no dragging and no double click. Note that this + * event is delayed by 250 ms to ensure that it is not a double click. + * @event ol.MapBrowserEvent#singleclick + * @api stable + */ + SINGLECLICK: 'singleclick', + + /** + * A click with no dragging. A double click will fire two of this. + * @event ol.MapBrowserEvent#click + * @api stable + */ + CLICK: ol.events.EventType.CLICK, + + /** + * A true double click, with no dragging. + * @event ol.MapBrowserEvent#dblclick + * @api stable + */ + DBLCLICK: ol.events.EventType.DBLCLICK, + + /** + * Triggered when a pointer is dragged. + * @event ol.MapBrowserEvent#pointerdrag + * @api + */ + POINTERDRAG: 'pointerdrag', + + /** + * Triggered when a pointer is moved. Note that on touch devices this is + * triggered when the map is panned, so is not the same as mousemove. + * @event ol.MapBrowserEvent#pointermove + * @api stable + */ + POINTERMOVE: 'pointermove', + + POINTERDOWN: 'pointerdown', + POINTERUP: 'pointerup', + POINTEROVER: 'pointerover', + POINTEROUT: 'pointerout', + POINTERENTER: 'pointerenter', + POINTERLEAVE: 'pointerleave', + POINTERCANCEL: 'pointercancel' +}; + +goog.provide('ol.layer.Base'); +goog.provide('ol.layer.LayerProperty'); + +goog.require('ol'); +goog.require('ol.Object'); +goog.require('ol.math'); +goog.require('ol.object'); +goog.require('ol.source.State'); + + +/** + * @enum {string} + */ +ol.layer.LayerProperty = { + OPACITY: 'opacity', + VISIBLE: 'visible', + EXTENT: 'extent', + Z_INDEX: 'zIndex', + MAX_RESOLUTION: 'maxResolution', + MIN_RESOLUTION: 'minResolution', + SOURCE: 'source' +}; + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Note that with `ol.layer.Base` and all its subclasses, any property set in + * the options is set as a {@link ol.Object} property on the layer object, so + * is observable, and has get/set accessors. + * + * @constructor + * @extends {ol.Object} + * @param {olx.layer.BaseOptions} options Layer options. + * @api stable + */ +ol.layer.Base = function(options) { + + ol.Object.call(this); + + /** + * @type {Object.<string, *>} + */ + var properties = ol.object.assign({}, options); + properties[ol.layer.LayerProperty.OPACITY] = + options.opacity !== undefined ? options.opacity : 1; + properties[ol.layer.LayerProperty.VISIBLE] = + options.visible !== undefined ? options.visible : true; + properties[ol.layer.LayerProperty.Z_INDEX] = + options.zIndex !== undefined ? options.zIndex : 0; + properties[ol.layer.LayerProperty.MAX_RESOLUTION] = + options.maxResolution !== undefined ? options.maxResolution : Infinity; + properties[ol.layer.LayerProperty.MIN_RESOLUTION] = + options.minResolution !== undefined ? options.minResolution : 0; + + this.setProperties(properties); +}; +ol.inherits(ol.layer.Base, ol.Object); + + +/** + * @return {ol.LayerState} Layer state. + */ +ol.layer.Base.prototype.getLayerState = function() { + var opacity = this.getOpacity(); + var sourceState = this.getSourceState(); + var visible = this.getVisible(); + var extent = this.getExtent(); + var zIndex = this.getZIndex(); + var maxResolution = this.getMaxResolution(); + var minResolution = this.getMinResolution(); + return { + layer: /** @type {ol.layer.Layer} */ (this), + opacity: ol.math.clamp(opacity, 0, 1), + sourceState: sourceState, + visible: visible, + managed: true, + extent: extent, + zIndex: zIndex, + maxResolution: maxResolution, + minResolution: Math.max(minResolution, 0) + }; +}; + + +/** + * @param {Array.<ol.layer.Layer>=} opt_array Array of layers (to be + * modified in place). + * @return {Array.<ol.layer.Layer>} Array of layers. + */ +ol.layer.Base.prototype.getLayersArray = goog.abstractMethod; + + +/** + * @param {Array.<ol.LayerState>=} opt_states Optional list of layer + * states (to be modified in place). + * @return {Array.<ol.LayerState>} List of layer states. + */ +ol.layer.Base.prototype.getLayerStatesArray = goog.abstractMethod; + + +/** + * Return the {@link ol.Extent extent} of the layer or `undefined` if it + * will be visible regardless of extent. + * @return {ol.Extent|undefined} The layer extent. + * @observable + * @api stable + */ +ol.layer.Base.prototype.getExtent = function() { + return /** @type {ol.Extent|undefined} */ ( + this.get(ol.layer.LayerProperty.EXTENT)); +}; + + +/** + * Return the maximum resolution of the layer. + * @return {number} The maximum resolution of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.getMaxResolution = function() { + return /** @type {number} */ ( + this.get(ol.layer.LayerProperty.MAX_RESOLUTION)); +}; + + +/** + * Return the minimum resolution of the layer. + * @return {number} The minimum resolution of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.getMinResolution = function() { + return /** @type {number} */ ( + this.get(ol.layer.LayerProperty.MIN_RESOLUTION)); +}; + + +/** + * Return the opacity of the layer (between 0 and 1). + * @return {number} The opacity of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.getOpacity = function() { + return /** @type {number} */ (this.get(ol.layer.LayerProperty.OPACITY)); +}; + + +/** + * @return {ol.source.State} Source state. + */ +ol.layer.Base.prototype.getSourceState = goog.abstractMethod; + + +/** + * Return the visibility of the layer (`true` or `false`). + * @return {boolean} The visibility of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.getVisible = function() { + return /** @type {boolean} */ (this.get(ol.layer.LayerProperty.VISIBLE)); +}; + + +/** + * Return the Z-index of the layer, which is used to order layers before + * rendering. The default Z-index is 0. + * @return {number} The Z-index of the layer. + * @observable + * @api + */ +ol.layer.Base.prototype.getZIndex = function() { + return /** @type {number} */ (this.get(ol.layer.LayerProperty.Z_INDEX)); +}; + + +/** + * Set the extent at which the layer is visible. If `undefined`, the layer + * will be visible at all extents. + * @param {ol.Extent|undefined} extent The extent of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.setExtent = function(extent) { + this.set(ol.layer.LayerProperty.EXTENT, extent); +}; + + +/** + * Set the maximum resolution at which the layer is visible. + * @param {number} maxResolution The maximum resolution of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.setMaxResolution = function(maxResolution) { + this.set(ol.layer.LayerProperty.MAX_RESOLUTION, maxResolution); +}; + + +/** + * Set the minimum resolution at which the layer is visible. + * @param {number} minResolution The minimum resolution of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.setMinResolution = function(minResolution) { + this.set(ol.layer.LayerProperty.MIN_RESOLUTION, minResolution); +}; + + +/** + * Set the opacity of the layer, allowed values range from 0 to 1. + * @param {number} opacity The opacity of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.setOpacity = function(opacity) { + this.set(ol.layer.LayerProperty.OPACITY, opacity); +}; + + +/** + * Set the visibility of the layer (`true` or `false`). + * @param {boolean} visible The visibility of the layer. + * @observable + * @api stable + */ +ol.layer.Base.prototype.setVisible = function(visible) { + this.set(ol.layer.LayerProperty.VISIBLE, visible); +}; + + +/** + * Set Z-index of the layer, which is used to order layers before rendering. + * The default Z-index is 0. + * @param {number} zindex The z-index of the layer. + * @observable + * @api + */ +ol.layer.Base.prototype.setZIndex = function(zindex) { + this.set(ol.layer.LayerProperty.Z_INDEX, zindex); +}; + +goog.provide('ol.render.VectorContext'); + + +/** + * Context for drawing geometries. A vector context is available on render + * events and does not need to be constructed directly. + * @constructor + * @struct + * @api + */ +ol.render.VectorContext = function() { +}; + + +/** + * Render a geometry. + * + * @param {ol.geom.Geometry} geometry The geometry to render. + */ +ol.render.VectorContext.prototype.drawGeometry = goog.abstractMethod; + + +/** + * Set the rendering style. + * + * @param {ol.style.Style} style The rendering style. + */ +ol.render.VectorContext.prototype.setStyle = goog.abstractMethod; + + +/** + * @param {ol.geom.Circle} circleGeometry Circle geometry. + * @param {ol.Feature} feature Feature, + */ +ol.render.VectorContext.prototype.drawCircle = goog.abstractMethod; + + +/** + * @param {ol.Feature} feature Feature. + * @param {ol.style.Style} style Style. + */ +ol.render.VectorContext.prototype.drawFeature = goog.abstractMethod; + + +/** + * @param {ol.geom.GeometryCollection} geometryCollectionGeometry Geometry + * collection. + * @param {ol.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawGeometryCollection = goog.abstractMethod; + + +/** + * @param {ol.geom.LineString|ol.render.Feature} lineStringGeometry Line + * string geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawLineString = goog.abstractMethod; + + +/** + * @param {ol.geom.MultiLineString|ol.render.Feature} multiLineStringGeometry + * MultiLineString geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawMultiLineString = goog.abstractMethod; + + +/** + * @param {ol.geom.MultiPoint|ol.render.Feature} multiPointGeometry MultiPoint + * geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawMultiPoint = goog.abstractMethod; + + +/** + * @param {ol.geom.MultiPolygon} multiPolygonGeometry MultiPolygon geometry. + * @param {ol.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawMultiPolygon = goog.abstractMethod; + + +/** + * @param {ol.geom.Point|ol.render.Feature} pointGeometry Point geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawPoint = goog.abstractMethod; + + +/** + * @param {ol.geom.Polygon|ol.render.Feature} polygonGeometry Polygon + * geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawPolygon = goog.abstractMethod; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.VectorContext.prototype.drawText = goog.abstractMethod; + + +/** + * @param {ol.style.Fill} fillStyle Fill style. + * @param {ol.style.Stroke} strokeStyle Stroke style. + */ +ol.render.VectorContext.prototype.setFillStrokeStyle = goog.abstractMethod; + + +/** + * @param {ol.style.Image} imageStyle Image style. + */ +ol.render.VectorContext.prototype.setImageStyle = goog.abstractMethod; + + +/** + * @param {ol.style.Text} textStyle Text style. + */ +ol.render.VectorContext.prototype.setTextStyle = goog.abstractMethod; + +goog.provide('ol.render.Event'); +goog.provide('ol.render.EventType'); + +goog.require('ol.events.Event'); +goog.require('ol.render.VectorContext'); + + +/** + * @enum {string} + */ +ol.render.EventType = { + /** + * @event ol.render.Event#postcompose + * @api + */ + POSTCOMPOSE: 'postcompose', + /** + * @event ol.render.Event#precompose + * @api + */ + PRECOMPOSE: 'precompose', + /** + * @event ol.render.Event#render + * @api + */ + RENDER: 'render' +}; + + +/** + * @constructor + * @extends {ol.events.Event} + * @implements {oli.render.Event} + * @param {ol.render.EventType} type Type. + * @param {Object=} opt_target Target. + * @param {ol.render.VectorContext=} opt_vectorContext Vector context. + * @param {olx.FrameState=} opt_frameState Frame state. + * @param {?CanvasRenderingContext2D=} opt_context Context. + * @param {?ol.webgl.Context=} opt_glContext WebGL Context. + */ +ol.render.Event = function( + type, opt_target, opt_vectorContext, opt_frameState, opt_context, + opt_glContext) { + + ol.events.Event.call(this, type, opt_target); + + /** + * For canvas, this is an instance of {@link ol.render.canvas.Immediate}. + * @type {ol.render.VectorContext|undefined} + * @api + */ + this.vectorContext = opt_vectorContext; + + /** + * An object representing the current render frame state. + * @type {olx.FrameState|undefined} + * @api + */ + this.frameState = opt_frameState; + + /** + * Canvas context. Only available when a Canvas renderer is used, null + * otherwise. + * @type {CanvasRenderingContext2D|null|undefined} + * @api + */ + this.context = opt_context; + + /** + * WebGL context. Only available when a WebGL renderer is used, null + * otherwise. + * @type {ol.webgl.Context|null|undefined} + * @api + */ + this.glContext = opt_glContext; + +}; +ol.inherits(ol.render.Event, ol.events.Event); + +goog.provide('ol.layer.Layer'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol'); +goog.require('ol.Object'); +goog.require('ol.layer.Base'); +goog.require('ol.layer.LayerProperty'); +goog.require('ol.object'); +goog.require('ol.render.EventType'); +goog.require('ol.source.State'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * A visual representation of raster or vector map data. + * Layers group together those properties that pertain to how the data is to be + * displayed, irrespective of the source of that data. + * + * Layers are usually added to a map with {@link ol.Map#addLayer}. Components + * like {@link ol.interaction.Select} use unmanaged layers internally. These + * unmanaged layers are associated with the map using + * {@link ol.layer.Layer#setMap} instead. + * + * A generic `change` event is fired when the state of the source changes. + * + * @constructor + * @extends {ol.layer.Base} + * @fires ol.render.Event + * @param {olx.layer.LayerOptions} options Layer options. + * @api stable + */ +ol.layer.Layer = function(options) { + + var baseOptions = ol.object.assign({}, options); + delete baseOptions.source; + + ol.layer.Base.call(this, /** @type {olx.layer.BaseOptions} */ (baseOptions)); + + /** + * @private + * @type {?ol.EventsKey} + */ + this.mapPrecomposeKey_ = null; + + /** + * @private + * @type {?ol.EventsKey} + */ + this.mapRenderKey_ = null; + + /** + * @private + * @type {?ol.EventsKey} + */ + this.sourceChangeKey_ = null; + + if (options.map) { + this.setMap(options.map); + } + + ol.events.listen(this, + ol.Object.getChangeEventType(ol.layer.LayerProperty.SOURCE), + this.handleSourcePropertyChange_, this); + + var source = options.source ? options.source : null; + this.setSource(source); +}; +ol.inherits(ol.layer.Layer, ol.layer.Base); + + +/** + * Return `true` if the layer is visible, and if the passed resolution is + * between the layer's minResolution and maxResolution. The comparison is + * inclusive for `minResolution` and exclusive for `maxResolution`. + * @param {ol.LayerState} layerState Layer state. + * @param {number} resolution Resolution. + * @return {boolean} The layer is visible at the given resolution. + */ +ol.layer.Layer.visibleAtResolution = function(layerState, resolution) { + return layerState.visible && resolution >= layerState.minResolution && + resolution < layerState.maxResolution; +}; + + +/** + * @inheritDoc + */ +ol.layer.Layer.prototype.getLayersArray = function(opt_array) { + var array = opt_array ? opt_array : []; + array.push(this); + return array; +}; + + +/** + * @inheritDoc + */ +ol.layer.Layer.prototype.getLayerStatesArray = function(opt_states) { + var states = opt_states ? opt_states : []; + states.push(this.getLayerState()); + return states; +}; + + +/** + * Get the layer source. + * @return {ol.source.Source} The layer source (or `null` if not yet set). + * @observable + * @api stable + */ +ol.layer.Layer.prototype.getSource = function() { + var source = this.get(ol.layer.LayerProperty.SOURCE); + return /** @type {ol.source.Source} */ (source) || null; +}; + + +/** + * @inheritDoc + */ +ol.layer.Layer.prototype.getSourceState = function() { + var source = this.getSource(); + return !source ? ol.source.State.UNDEFINED : source.getState(); +}; + + +/** + * @private + */ +ol.layer.Layer.prototype.handleSourceChange_ = function() { + this.changed(); +}; + + +/** + * @private + */ +ol.layer.Layer.prototype.handleSourcePropertyChange_ = function() { + if (this.sourceChangeKey_) { + ol.events.unlistenByKey(this.sourceChangeKey_); + this.sourceChangeKey_ = null; + } + var source = this.getSource(); + if (source) { + this.sourceChangeKey_ = ol.events.listen(source, + ol.events.EventType.CHANGE, this.handleSourceChange_, this); + } + this.changed(); +}; + + +/** + * Sets the layer to be rendered on top of other layers on a map. The map will + * not manage this layer in its layers collection, and the callback in + * {@link ol.Map#forEachLayerAtPixel} will receive `null` as layer. This + * is useful for temporary layers. To remove an unmanaged layer from the map, + * use `#setMap(null)`. + * + * To add the layer to a map and have it managed by the map, use + * {@link ol.Map#addLayer} instead. + * @param {ol.Map} map Map. + * @api + */ +ol.layer.Layer.prototype.setMap = function(map) { + if (this.mapPrecomposeKey_) { + ol.events.unlistenByKey(this.mapPrecomposeKey_); + this.mapPrecomposeKey_ = null; + } + if (!map) { + this.changed(); + } + if (this.mapRenderKey_) { + ol.events.unlistenByKey(this.mapRenderKey_); + this.mapRenderKey_ = null; + } + if (map) { + this.mapPrecomposeKey_ = ol.events.listen( + map, ol.render.EventType.PRECOMPOSE, function(evt) { + var layerState = this.getLayerState(); + layerState.managed = false; + layerState.zIndex = Infinity; + evt.frameState.layerStatesArray.push(layerState); + evt.frameState.layerStates[goog.getUid(this)] = layerState; + }, this); + this.mapRenderKey_ = ol.events.listen( + this, ol.events.EventType.CHANGE, map.render, map); + this.changed(); + } +}; + + +/** + * Set the layer source. + * @param {ol.source.Source} source The layer source. + * @observable + * @api stable + */ +ol.layer.Layer.prototype.setSource = function(source) { + this.set(ol.layer.LayerProperty.SOURCE, source); +}; + +goog.provide('ol.ImageBase'); +goog.provide('ol.ImageState'); + +goog.require('goog.asserts'); +goog.require('ol.events.EventTarget'); +goog.require('ol.events.EventType'); +goog.require('ol.Attribution'); + + +/** + * @enum {number} + */ +ol.ImageState = { + IDLE: 0, + LOADING: 1, + LOADED: 2, + ERROR: 3 +}; + + +/** + * @constructor + * @extends {ol.events.EventTarget} + * @param {ol.Extent} extent Extent. + * @param {number|undefined} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.ImageState} state State. + * @param {Array.<ol.Attribution>} attributions Attributions. + */ +ol.ImageBase = function(extent, resolution, pixelRatio, state, attributions) { + + ol.events.EventTarget.call(this); + + /** + * @private + * @type {Array.<ol.Attribution>} + */ + this.attributions_ = attributions; + + /** + * @protected + * @type {ol.Extent} + */ + this.extent = extent; + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; + + /** + * @protected + * @type {number|undefined} + */ + this.resolution = resolution; + + /** + * @protected + * @type {ol.ImageState} + */ + this.state = state; + +}; +ol.inherits(ol.ImageBase, ol.events.EventTarget); + + +/** + * @protected + */ +ol.ImageBase.prototype.changed = function() { + this.dispatchEvent(ol.events.EventType.CHANGE); +}; + + +/** + * @return {Array.<ol.Attribution>} Attributions. + */ +ol.ImageBase.prototype.getAttributions = function() { + return this.attributions_; +}; + + +/** + * @return {ol.Extent} Extent. + */ +ol.ImageBase.prototype.getExtent = function() { + return this.extent; +}; + + +/** + * @param {Object=} opt_context Object. + * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image. + */ +ol.ImageBase.prototype.getImage = goog.abstractMethod; + + +/** + * @return {number} PixelRatio. + */ +ol.ImageBase.prototype.getPixelRatio = function() { + return this.pixelRatio_; +}; + + +/** + * @return {number} Resolution. + */ +ol.ImageBase.prototype.getResolution = function() { + goog.asserts.assert(this.resolution !== undefined, 'resolution not yet set'); + return this.resolution; +}; + + +/** + * @return {ol.ImageState} State. + */ +ol.ImageBase.prototype.getState = function() { + return this.state; +}; + + +/** + * Load not yet loaded URI. + */ +ol.ImageBase.prototype.load = goog.abstractMethod; + +goog.provide('ol.vec.Mat4'); +goog.provide('ol.vec.Mat4.Number'); + +goog.require('goog.vec.Mat4'); + + +/** + * A alias for the goog.vec.Number type. + * @typedef {goog.vec.Number} + */ +ol.vec.Mat4.Number; + + +/** + * @param {!goog.vec.Mat4.Number} mat Matrix. + * @param {number} translateX1 Translate X1. + * @param {number} translateY1 Translate Y1. + * @param {number} scaleX Scale X. + * @param {number} scaleY Scale Y. + * @param {number} rotation Rotation. + * @param {number} translateX2 Translate X2. + * @param {number} translateY2 Translate Y2. + * @return {!goog.vec.Mat4.Number} Matrix. + */ +ol.vec.Mat4.makeTransform2D = function(mat, translateX1, translateY1, + scaleX, scaleY, rotation, translateX2, translateY2) { + goog.vec.Mat4.makeIdentity(mat); + if (translateX1 !== 0 || translateY1 !== 0) { + goog.vec.Mat4.translate(mat, translateX1, translateY1, 0); + } + if (scaleX != 1 || scaleY != 1) { + goog.vec.Mat4.scale(mat, scaleX, scaleY, 1); + } + if (rotation !== 0) { + goog.vec.Mat4.rotateZ(mat, rotation); + } + if (translateX2 !== 0 || translateY2 !== 0) { + goog.vec.Mat4.translate(mat, translateX2, translateY2, 0); + } + return mat; +}; + + +/** + * Returns true if mat1 and mat2 represent the same 2D transformation. + * @param {goog.vec.Mat4.Number} mat1 Matrix 1. + * @param {goog.vec.Mat4.Number} mat2 Matrix 2. + * @return {boolean} Equal 2D. + */ +ol.vec.Mat4.equals2D = function(mat1, mat2) { + return ( + goog.vec.Mat4.getElement(mat1, 0, 0) == + goog.vec.Mat4.getElement(mat2, 0, 0) && + goog.vec.Mat4.getElement(mat1, 1, 0) == + goog.vec.Mat4.getElement(mat2, 1, 0) && + goog.vec.Mat4.getElement(mat1, 0, 1) == + goog.vec.Mat4.getElement(mat2, 0, 1) && + goog.vec.Mat4.getElement(mat1, 1, 1) == + goog.vec.Mat4.getElement(mat2, 1, 1) && + goog.vec.Mat4.getElement(mat1, 0, 3) == + goog.vec.Mat4.getElement(mat2, 0, 3) && + goog.vec.Mat4.getElement(mat1, 1, 3) == + goog.vec.Mat4.getElement(mat2, 1, 3)); +}; + + +/** + * Transforms the given vector with the given matrix storing the resulting, + * transformed vector into resultVec. The input vector is multiplied against the + * upper 2x4 matrix omitting the projective component. + * + * @param {goog.vec.Mat4.Number} mat The matrix supplying the transformation. + * @param {Array.<number>} vec The 3 element vector to transform. + * @param {Array.<number>} resultVec The 3 element vector to receive the results + * (may be vec). + * @return {Array.<number>} return resultVec so that operations can be + * chained together. + */ +ol.vec.Mat4.multVec2 = function(mat, vec, resultVec) { + var m00 = goog.vec.Mat4.getElement(mat, 0, 0); + var m10 = goog.vec.Mat4.getElement(mat, 1, 0); + var m01 = goog.vec.Mat4.getElement(mat, 0, 1); + var m11 = goog.vec.Mat4.getElement(mat, 1, 1); + var m03 = goog.vec.Mat4.getElement(mat, 0, 3); + var m13 = goog.vec.Mat4.getElement(mat, 1, 3); + var x = vec[0], y = vec[1]; + resultVec[0] = m00 * x + m01 * y + m03; + resultVec[1] = m10 * x + m11 * y + m13; + return resultVec; +}; + +goog.provide('ol.renderer.Layer'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol'); +goog.require('ol.functions'); +goog.require('ol.ImageState'); +goog.require('ol.Observable'); +goog.require('ol.TileRange'); +goog.require('ol.TileState'); +goog.require('ol.layer.Layer'); +goog.require('ol.source.Source'); +goog.require('ol.source.State'); +goog.require('ol.source.Tile'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.Observable} + * @param {ol.layer.Layer} layer Layer. + * @struct + */ +ol.renderer.Layer = function(layer) { + + ol.Observable.call(this); + + /** + * @private + * @type {ol.layer.Layer} + */ + this.layer_ = layer; + + +}; +ol.inherits(ol.renderer.Layer, ol.Observable); + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {olx.FrameState} frameState Frame state. + * @param {function(this: S, (ol.Feature|ol.render.Feature), ol.layer.Layer): T} + * callback Feature callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T + */ +ol.renderer.Layer.prototype.forEachFeatureAtCoordinate = ol.nullFunction; + + +/** + * @param {ol.Pixel} pixel Pixel. + * @param {olx.FrameState} frameState Frame state. + * @param {function(this: S, ol.layer.Layer): T} callback Layer callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @return {T|undefined} Callback result. + * @template S,T + */ +ol.renderer.Layer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { + var coordinate = pixel.slice(); + ol.vec.Mat4.multVec2( + frameState.pixelToCoordinateMatrix, coordinate, coordinate); + + var hasFeature = this.forEachFeatureAtCoordinate( + coordinate, frameState, ol.functions.TRUE, this); + + if (hasFeature) { + return callback.call(thisArg, this.layer_); + } else { + return undefined; + } +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {olx.FrameState} frameState Frame state. + * @return {boolean} Is there a feature at the given coordinate? + */ +ol.renderer.Layer.prototype.hasFeatureAtCoordinate = ol.functions.FALSE; + + +/** + * Create a function that adds loaded tiles to the tile lookup. + * @param {ol.source.Tile} source Tile source. + * @param {ol.proj.Projection} projection Projection of the tiles. + * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded + * tiles by zoom level. + * @return {function(number, ol.TileRange):boolean} A function that can be + * called with a zoom level and a tile range to add loaded tiles to the + * lookup. + * @protected + */ +ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, projection, tiles) { + return ( + /** + * @param {number} zoom Zoom level. + * @param {ol.TileRange} tileRange Tile range. + * @return {boolean} The tile range is fully loaded. + */ + function(zoom, tileRange) { + function callback(tile) { + if (!tiles[zoom]) { + tiles[zoom] = {}; + } + tiles[zoom][tile.tileCoord.toString()] = tile; + } + return source.forEachLoadedTile(projection, zoom, tileRange, callback); + }); +}; + + +/** + * @return {ol.layer.Layer} Layer. + */ +ol.renderer.Layer.prototype.getLayer = function() { + return this.layer_; +}; + + +/** + * Handle changes in image state. + * @param {ol.events.Event} event Image change event. + * @private + */ +ol.renderer.Layer.prototype.handleImageChange_ = function(event) { + var image = /** @type {ol.Image} */ (event.target); + if (image.getState() === ol.ImageState.LOADED) { + this.renderIfReadyAndVisible(); + } +}; + + +/** + * Load the image if not already loaded, and register the image change + * listener if needed. + * @param {ol.ImageBase} image Image. + * @return {boolean} `true` if the image is already loaded, `false` + * otherwise. + * @protected + */ +ol.renderer.Layer.prototype.loadImage = function(image) { + var imageState = image.getState(); + if (imageState != ol.ImageState.LOADED && + imageState != ol.ImageState.ERROR) { + // the image is either "idle" or "loading", register the change + // listener (a noop if the listener was already registered) + goog.asserts.assert(imageState == ol.ImageState.IDLE || + imageState == ol.ImageState.LOADING, + 'imageState is "idle" or "loading"'); + ol.events.listen(image, ol.events.EventType.CHANGE, + this.handleImageChange_, this); + } + if (imageState == ol.ImageState.IDLE) { + image.load(); + imageState = image.getState(); + goog.asserts.assert(imageState == ol.ImageState.LOADING || + imageState == ol.ImageState.LOADED, + 'imageState is "loading" or "loaded"'); + } + return imageState == ol.ImageState.LOADED; +}; + + +/** + * @protected + */ +ol.renderer.Layer.prototype.renderIfReadyAndVisible = function() { + var layer = this.getLayer(); + if (layer.getVisible() && layer.getSourceState() == ol.source.State.READY) { + this.changed(); + } +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.source.Tile} tileSource Tile source. + * @protected + */ +ol.renderer.Layer.prototype.scheduleExpireCache = function(frameState, tileSource) { + if (tileSource.canExpireCache()) { + /** + * @param {ol.source.Tile} tileSource Tile source. + * @param {ol.Map} map Map. + * @param {olx.FrameState} frameState Frame state. + */ + var postRenderFunction = function(tileSource, map, frameState) { + var tileSourceKey = goog.getUid(tileSource).toString(); + tileSource.expireCache(frameState.viewState.projection, + frameState.usedTiles[tileSourceKey]); + }.bind(null, tileSource); + + frameState.postRenderFunctions.push( + /** @type {ol.PostRenderFunction} */ (postRenderFunction) + ); + } +}; + + +/** + * @param {Object.<string, ol.Attribution>} attributionsSet Attributions + * set (target). + * @param {Array.<ol.Attribution>} attributions Attributions (source). + * @protected + */ +ol.renderer.Layer.prototype.updateAttributions = function(attributionsSet, attributions) { + if (attributions) { + var attribution, i, ii; + for (i = 0, ii = attributions.length; i < ii; ++i) { + attribution = attributions[i]; + attributionsSet[goog.getUid(attribution).toString()] = attribution; + } + } +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.source.Source} source Source. + * @protected + */ +ol.renderer.Layer.prototype.updateLogos = function(frameState, source) { + var logo = source.getLogo(); + if (logo !== undefined) { + if (typeof logo === 'string') { + frameState.logos[logo] = ''; + } else if (goog.isObject(logo)) { + goog.asserts.assertString(logo.href, 'logo.href is a string'); + goog.asserts.assertString(logo.src, 'logo.src is a string'); + frameState.logos[logo.src] = logo.href; + } + } +}; + + +/** + * @param {Object.<string, Object.<string, ol.TileRange>>} usedTiles Used tiles. + * @param {ol.source.Tile} tileSource Tile source. + * @param {number} z Z. + * @param {ol.TileRange} tileRange Tile range. + * @protected + */ +ol.renderer.Layer.prototype.updateUsedTiles = function(usedTiles, tileSource, z, tileRange) { + // FIXME should we use tilesToDrawByZ instead? + var tileSourceKey = goog.getUid(tileSource).toString(); + var zKey = z.toString(); + if (tileSourceKey in usedTiles) { + if (zKey in usedTiles[tileSourceKey]) { + usedTiles[tileSourceKey][zKey].extend(tileRange); + } else { + usedTiles[tileSourceKey][zKey] = tileRange; + } + } else { + usedTiles[tileSourceKey] = {}; + usedTiles[tileSourceKey][zKey] = tileRange; + } +}; + + +/** + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {ol.Size} size Size. + * @protected + * @return {ol.Coordinate} Snapped center. + */ +ol.renderer.Layer.prototype.snapCenterToPixel = function(center, resolution, size) { + return [ + resolution * (Math.round(center[0] / resolution) + (size[0] % 2) / 2), + resolution * (Math.round(center[1] / resolution) + (size[1] % 2) / 2) + ]; +}; + + +/** + * Manage tile pyramid. + * This function performs a number of functions related to the tiles at the + * current zoom and lower zoom levels: + * - registers idle tiles in frameState.wantedTiles so that they are not + * discarded by the tile queue + * - enqueues missing tiles + * @param {olx.FrameState} frameState Frame state. + * @param {ol.source.Tile} tileSource Tile source. + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @param {ol.Extent} extent Extent. + * @param {number} currentZ Current Z. + * @param {number} preload Load low resolution tiles up to 'preload' levels. + * @param {function(this: T, ol.Tile)=} opt_tileCallback Tile callback. + * @param {T=} opt_this Object to use as `this` in `opt_tileCallback`. + * @protected + * @template T + */ +ol.renderer.Layer.prototype.manageTilePyramid = function( + frameState, tileSource, tileGrid, pixelRatio, projection, extent, + currentZ, preload, opt_tileCallback, opt_this) { + var tileSourceKey = goog.getUid(tileSource).toString(); + if (!(tileSourceKey in frameState.wantedTiles)) { + frameState.wantedTiles[tileSourceKey] = {}; + } + var wantedTiles = frameState.wantedTiles[tileSourceKey]; + var tileQueue = frameState.tileQueue; + var minZoom = tileGrid.getMinZoom(); + var tile, tileRange, tileResolution, x, y, z; + for (z = currentZ; z >= minZoom; --z) { + tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z, tileRange); + tileResolution = tileGrid.getResolution(z); + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + if (currentZ - z <= preload) { + tile = tileSource.getTile(z, x, y, pixelRatio, projection); + if (tile.getState() == ol.TileState.IDLE) { + wantedTiles[tile.tileCoord.toString()] = true; + if (!tileQueue.isKeyQueued(tile.getKey())) { + tileQueue.enqueue([tile, tileSourceKey, + tileGrid.getTileCoordCenter(tile.tileCoord), tileResolution]); + } + } + if (opt_tileCallback !== undefined) { + opt_tileCallback.call(opt_this, tile); + } + } else { + tileSource.useTile(z, x, y, projection); + } + } + } + } +}; + +goog.provide('ol.style.Image'); +goog.provide('ol.style.ImageState'); + + +/** + * @enum {number} + */ +ol.style.ImageState = { + IDLE: 0, + LOADING: 1, + LOADED: 2, + ERROR: 3 +}; + + +/** + * @classdesc + * A base class used for creating subclasses and not instantiated in + * apps. Base class for {@link ol.style.Icon}, {@link ol.style.Circle} and + * {@link ol.style.RegularShape}. + * + * @constructor + * @param {ol.StyleImageOptions} options Options. + * @api + */ +ol.style.Image = function(options) { + + /** + * @private + * @type {number} + */ + this.opacity_ = options.opacity; + + /** + * @private + * @type {boolean} + */ + this.rotateWithView_ = options.rotateWithView; + + /** + * @private + * @type {number} + */ + this.rotation_ = options.rotation; + + /** + * @private + * @type {number} + */ + this.scale_ = options.scale; + + /** + * @private + * @type {boolean} + */ + this.snapToPixel_ = options.snapToPixel; + +}; + + +/** + * Get the symbolizer opacity. + * @return {number} Opacity. + * @api + */ +ol.style.Image.prototype.getOpacity = function() { + return this.opacity_; +}; + + +/** + * Determine whether the symbolizer rotates with the map. + * @return {boolean} Rotate with map. + * @api + */ +ol.style.Image.prototype.getRotateWithView = function() { + return this.rotateWithView_; +}; + + +/** + * Get the symoblizer rotation. + * @return {number} Rotation. + * @api + */ +ol.style.Image.prototype.getRotation = function() { + return this.rotation_; +}; + + +/** + * Get the symbolizer scale. + * @return {number} Scale. + * @api + */ +ol.style.Image.prototype.getScale = function() { + return this.scale_; +}; + + +/** + * Determine whether the symbolizer should be snapped to a pixel. + * @return {boolean} The symbolizer should snap to a pixel. + * @api + */ +ol.style.Image.prototype.getSnapToPixel = function() { + return this.snapToPixel_; +}; + + +/** + * Get the anchor point. The anchor determines the center point for the + * symbolizer. Its units are determined by `anchorXUnits` and `anchorYUnits`. + * @function + * @return {Array.<number>} Anchor. + */ +ol.style.Image.prototype.getAnchor = goog.abstractMethod; + + +/** + * Get the image element for the symbolizer. + * @function + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element. + */ +ol.style.Image.prototype.getImage = goog.abstractMethod; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLCanvasElement|HTMLVideoElement|Image} Image element. + */ +ol.style.Image.prototype.getHitDetectionImage = goog.abstractMethod; + + +/** + * @return {ol.style.ImageState} Image state. + */ +ol.style.Image.prototype.getImageState = goog.abstractMethod; + + +/** + * @return {ol.Size} Image size. + */ +ol.style.Image.prototype.getImageSize = goog.abstractMethod; + + +/** + * @return {ol.Size} Size of the hit-detection image. + */ +ol.style.Image.prototype.getHitDetectionImageSize = goog.abstractMethod; + + +/** + * Get the origin of the symbolizer. + * @function + * @return {Array.<number>} Origin. + */ +ol.style.Image.prototype.getOrigin = goog.abstractMethod; + + +/** + * Get the size of the symbolizer (in pixels). + * @function + * @return {ol.Size} Size. + */ +ol.style.Image.prototype.getSize = goog.abstractMethod; + + +/** + * Set the opacity. + * + * @param {number} opacity Opacity. + * @api + */ +ol.style.Image.prototype.setOpacity = function(opacity) { + this.opacity_ = opacity; +}; + + +/** + * Set whether to rotate the style with the view. + * + * @param {boolean} rotateWithView Rotate with map. + */ +ol.style.Image.prototype.setRotateWithView = function(rotateWithView) { + this.rotateWithView_ = rotateWithView; +}; + + +/** + * Set the rotation. + * + * @param {number} rotation Rotation. + * @api + */ +ol.style.Image.prototype.setRotation = function(rotation) { + this.rotation_ = rotation; +}; + + +/** + * Set the scale. + * + * @param {number} scale Scale. + * @api + */ +ol.style.Image.prototype.setScale = function(scale) { + this.scale_ = scale; +}; + + +/** + * Set whether to snap the image to the closest pixel. + * + * @param {boolean} snapToPixel Snap to pixel? + */ +ol.style.Image.prototype.setSnapToPixel = function(snapToPixel) { + this.snapToPixel_ = snapToPixel; +}; + + +/** + * @param {function(this: T, ol.events.Event)} listener Listener function. + * @param {T} thisArg Value to use as `this` when executing `listener`. + * @return {ol.EventsKey|undefined} Listener key. + * @template T + */ +ol.style.Image.prototype.listenImageChange = goog.abstractMethod; + + +/** + * Load not yet loaded URI. + */ +ol.style.Image.prototype.load = goog.abstractMethod; + + +/** + * @param {function(this: T, ol.events.Event)} listener Listener function. + * @param {T} thisArg Value to use as `this` when executing `listener`. + * @template T + */ +ol.style.Image.prototype.unlistenImageChange = goog.abstractMethod; + +goog.provide('ol.style.Icon'); +goog.provide('ol.style.IconAnchorUnits'); +goog.provide('ol.style.IconImageCache'); +goog.provide('ol.style.IconOrigin'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventTarget'); +goog.require('ol.events.EventType'); +goog.require('ol.color'); +goog.require('ol.dom'); +goog.require('ol.style.Image'); +goog.require('ol.style.ImageState'); + + +/** + * Icon anchor units. One of 'fraction', 'pixels'. + * @enum {string} + */ +ol.style.IconAnchorUnits = { + FRACTION: 'fraction', + PIXELS: 'pixels' +}; + + +/** + * Icon origin. One of 'bottom-left', 'bottom-right', 'top-left', 'top-right'. + * @enum {string} + */ +ol.style.IconOrigin = { + BOTTOM_LEFT: 'bottom-left', + BOTTOM_RIGHT: 'bottom-right', + TOP_LEFT: 'top-left', + TOP_RIGHT: 'top-right' +}; + + +/** + * @classdesc + * Set icon style for vector features. + * + * @constructor + * @param {olx.style.IconOptions=} opt_options Options. + * @extends {ol.style.Image} + * @api + */ +ol.style.Icon = function(opt_options) { + + var options = opt_options || {}; + + /** + * @private + * @type {Array.<number>} + */ + this.anchor_ = options.anchor !== undefined ? options.anchor : [0.5, 0.5]; + + /** + * @private + * @type {Array.<number>} + */ + this.normalizedAnchor_ = null; + + /** + * @private + * @type {ol.style.IconOrigin} + */ + this.anchorOrigin_ = options.anchorOrigin !== undefined ? + options.anchorOrigin : ol.style.IconOrigin.TOP_LEFT; + + /** + * @private + * @type {ol.style.IconAnchorUnits} + */ + this.anchorXUnits_ = options.anchorXUnits !== undefined ? + options.anchorXUnits : ol.style.IconAnchorUnits.FRACTION; + + /** + * @private + * @type {ol.style.IconAnchorUnits} + */ + this.anchorYUnits_ = options.anchorYUnits !== undefined ? + options.anchorYUnits : ol.style.IconAnchorUnits.FRACTION; + + /** + * @type {?string} + */ + var crossOrigin = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @type {Image|HTMLCanvasElement} + */ + var image = options.img !== undefined ? options.img : null; + + /** + * @type {ol.Size} + */ + var imgSize = options.imgSize !== undefined ? options.imgSize : null; + + /** + * @type {string|undefined} + */ + var src = options.src; + + goog.asserts.assert(!(src !== undefined && image), + 'image and src can not provided at the same time'); + goog.asserts.assert( + !image || (image && imgSize), + 'imgSize must be set when image is provided'); + + if ((src === undefined || src.length === 0) && image) { + src = image.src || goog.getUid(image).toString(); + } + goog.asserts.assert(src !== undefined && src.length > 0, + 'must provide a defined and non-empty src or image'); + + /** + * @type {ol.style.ImageState} + */ + var imageState = options.src !== undefined ? + ol.style.ImageState.IDLE : ol.style.ImageState.LOADED; + + /** + * @type {ol.Color} + */ + var color = options.color !== undefined ? ol.color.asArray(options.color) : + null; + + /** + * @private + * @type {ol.style.IconImage_} + */ + this.iconImage_ = ol.style.IconImage_.get( + image, src, imgSize, crossOrigin, imageState, color); + + /** + * @private + * @type {Array.<number>} + */ + this.offset_ = options.offset !== undefined ? options.offset : [0, 0]; + + /** + * @private + * @type {ol.style.IconOrigin} + */ + this.offsetOrigin_ = options.offsetOrigin !== undefined ? + options.offsetOrigin : ol.style.IconOrigin.TOP_LEFT; + + /** + * @private + * @type {Array.<number>} + */ + this.origin_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = options.size !== undefined ? options.size : null; + + /** + * @type {number} + */ + var opacity = options.opacity !== undefined ? options.opacity : 1; + + /** + * @type {boolean} + */ + var rotateWithView = options.rotateWithView !== undefined ? + options.rotateWithView : false; + + /** + * @type {number} + */ + var rotation = options.rotation !== undefined ? options.rotation : 0; + + /** + * @type {number} + */ + var scale = options.scale !== undefined ? options.scale : 1; + + /** + * @type {boolean} + */ + var snapToPixel = options.snapToPixel !== undefined ? + options.snapToPixel : true; + + ol.style.Image.call(this, { + opacity: opacity, + rotation: rotation, + scale: scale, + snapToPixel: snapToPixel, + rotateWithView: rotateWithView + }); + +}; +ol.inherits(ol.style.Icon, ol.style.Image); + + +/** + * @inheritDoc + * @api + */ +ol.style.Icon.prototype.getAnchor = function() { + if (this.normalizedAnchor_) { + return this.normalizedAnchor_; + } + var anchor = this.anchor_; + var size = this.getSize(); + if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION || + this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { + if (!size) { + return null; + } + anchor = this.anchor_.slice(); + if (this.anchorXUnits_ == ol.style.IconAnchorUnits.FRACTION) { + anchor[0] *= size[0]; + } + if (this.anchorYUnits_ == ol.style.IconAnchorUnits.FRACTION) { + anchor[1] *= size[1]; + } + } + + if (this.anchorOrigin_ != ol.style.IconOrigin.TOP_LEFT) { + if (!size) { + return null; + } + if (anchor === this.anchor_) { + anchor = this.anchor_.slice(); + } + if (this.anchorOrigin_ == ol.style.IconOrigin.TOP_RIGHT || + this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { + anchor[0] = -anchor[0] + size[0]; + } + if (this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT || + this.anchorOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { + anchor[1] = -anchor[1] + size[1]; + } + } + this.normalizedAnchor_ = anchor; + return this.normalizedAnchor_; +}; + + +/** + * Get the image icon. + * @param {number} pixelRatio Pixel ratio. + * @return {Image|HTMLCanvasElement} Image or Canvas element. + * @api + */ +ol.style.Icon.prototype.getImage = function(pixelRatio) { + return this.iconImage_.getImage(pixelRatio); +}; + + +/** + * Real Image size used. + * @return {ol.Size} Size. + */ +ol.style.Icon.prototype.getImageSize = function() { + return this.iconImage_.getSize(); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.getHitDetectionImageSize = function() { + return this.getImageSize(); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.getImageState = function() { + return this.iconImage_.getImageState(); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.getHitDetectionImage = function(pixelRatio) { + return this.iconImage_.getHitDetectionImage(pixelRatio); +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.Icon.prototype.getOrigin = function() { + if (this.origin_) { + return this.origin_; + } + var offset = this.offset_; + + if (this.offsetOrigin_ != ol.style.IconOrigin.TOP_LEFT) { + var size = this.getSize(); + var iconImageSize = this.iconImage_.getSize(); + if (!size || !iconImageSize) { + return null; + } + offset = offset.slice(); + if (this.offsetOrigin_ == ol.style.IconOrigin.TOP_RIGHT || + this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { + offset[0] = iconImageSize[0] - size[0] - offset[0]; + } + if (this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_LEFT || + this.offsetOrigin_ == ol.style.IconOrigin.BOTTOM_RIGHT) { + offset[1] = iconImageSize[1] - size[1] - offset[1]; + } + } + this.origin_ = offset; + return this.origin_; +}; + + +/** + * Get the image URL. + * @return {string|undefined} Image src. + * @api + */ +ol.style.Icon.prototype.getSrc = function() { + return this.iconImage_.getSrc(); +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.Icon.prototype.getSize = function() { + return !this.size_ ? this.iconImage_.getSize() : this.size_; +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.listenImageChange = function(listener, thisArg) { + return ol.events.listen(this.iconImage_, ol.events.EventType.CHANGE, + listener, thisArg); +}; + + +/** + * Load not yet loaded URI. + * When rendering a feature with an icon style, the vector renderer will + * automatically call this method. However, you might want to call this + * method yourself for preloading or other purposes. + * @api + */ +ol.style.Icon.prototype.load = function() { + this.iconImage_.load(); +}; + + +/** + * @inheritDoc + */ +ol.style.Icon.prototype.unlistenImageChange = function(listener, thisArg) { + ol.events.unlisten(this.iconImage_, ol.events.EventType.CHANGE, + listener, thisArg); +}; + + +/** + * @constructor + * @param {Image|HTMLCanvasElement} image Image. + * @param {string|undefined} src Src. + * @param {ol.Size} size Size. + * @param {?string} crossOrigin Cross origin. + * @param {ol.style.ImageState} imageState Image state. + * @param {ol.Color} color Color. + * @extends {ol.events.EventTarget} + * @private + */ +ol.style.IconImage_ = function(image, src, size, crossOrigin, imageState, + color) { + + ol.events.EventTarget.call(this); + + /** + * @private + * @type {Image|HTMLCanvasElement} + */ + this.hitDetectionImage_ = null; + + /** + * @private + * @type {Image|HTMLCanvasElement} + */ + this.image_ = !image ? new Image() : image; + + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = color ? + /** @type {HTMLCanvasElement} */ (document.createElement('CANVAS')) : + null; + + /** + * @private + * @type {ol.Color} + */ + this.color_ = color; + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.imageListenerKeys_ = null; + + /** + * @private + * @type {ol.style.ImageState} + */ + this.imageState_ = imageState; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = size; + + /** + * @private + * @type {string|undefined} + */ + this.src_ = src; + + /** + * @private + * @type {boolean} + */ + this.tainting_ = false; + if (this.imageState_ == ol.style.ImageState.LOADED) { + this.determineTainting_(); + } + +}; +ol.inherits(ol.style.IconImage_, ol.events.EventTarget); + + +/** + * @param {Image|HTMLCanvasElement} image Image. + * @param {string} src Src. + * @param {ol.Size} size Size. + * @param {?string} crossOrigin Cross origin. + * @param {ol.style.ImageState} imageState Image state. + * @param {ol.Color} color Color. + * @return {ol.style.IconImage_} Icon image. + */ +ol.style.IconImage_.get = function(image, src, size, crossOrigin, imageState, + color) { + var iconImageCache = ol.style.IconImageCache.getInstance(); + var iconImage = iconImageCache.get(src, crossOrigin, color); + if (!iconImage) { + iconImage = new ol.style.IconImage_( + image, src, size, crossOrigin, imageState, color); + iconImageCache.set(src, crossOrigin, color, iconImage); + } + return iconImage; +}; + + +/** + * @private + */ +ol.style.IconImage_.prototype.determineTainting_ = function() { + var context = ol.dom.createCanvasContext2D(1, 1); + try { + context.drawImage(this.image_, 0, 0); + context.getImageData(0, 0, 1, 1); + } catch (e) { + this.tainting_ = true; + } +}; + + +/** + * @private + */ +ol.style.IconImage_.prototype.dispatchChangeEvent_ = function() { + this.dispatchEvent(ol.events.EventType.CHANGE); +}; + + +/** + * @private + */ +ol.style.IconImage_.prototype.handleImageError_ = function() { + this.imageState_ = ol.style.ImageState.ERROR; + this.unlistenImage_(); + this.dispatchChangeEvent_(); +}; + + +/** + * @private + */ +ol.style.IconImage_.prototype.handleImageLoad_ = function() { + this.imageState_ = ol.style.ImageState.LOADED; + if (this.size_) { + this.image_.width = this.size_[0]; + this.image_.height = this.size_[1]; + } + this.size_ = [this.image_.width, this.image_.height]; + this.unlistenImage_(); + this.determineTainting_(); + this.replaceColor_(); + this.dispatchChangeEvent_(); +}; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {Image|HTMLCanvasElement} Image or Canvas element. + */ +ol.style.IconImage_.prototype.getImage = function(pixelRatio) { + return this.canvas_ ? this.canvas_ : this.image_; +}; + + +/** + * @return {ol.style.ImageState} Image state. + */ +ol.style.IconImage_.prototype.getImageState = function() { + return this.imageState_; +}; + + +/** + * @param {number} pixelRatio Pixel ratio. + * @return {Image|HTMLCanvasElement} Image element. + */ +ol.style.IconImage_.prototype.getHitDetectionImage = function(pixelRatio) { + if (!this.hitDetectionImage_) { + if (this.tainting_) { + var width = this.size_[0]; + var height = this.size_[1]; + var context = ol.dom.createCanvasContext2D(width, height); + context.fillRect(0, 0, width, height); + this.hitDetectionImage_ = context.canvas; + } else { + this.hitDetectionImage_ = this.image_; + } + } + return this.hitDetectionImage_; +}; + + +/** + * @return {ol.Size} Image size. + */ +ol.style.IconImage_.prototype.getSize = function() { + return this.size_; +}; + + +/** + * @return {string|undefined} Image src. + */ +ol.style.IconImage_.prototype.getSrc = function() { + return this.src_; +}; + + +/** + * Load not yet loaded URI. + */ +ol.style.IconImage_.prototype.load = function() { + if (this.imageState_ == ol.style.ImageState.IDLE) { + goog.asserts.assert(this.src_ !== undefined, + 'this.src_ must not be undefined'); + goog.asserts.assert(!this.imageListenerKeys_, + 'no listener keys existing'); + this.imageState_ = ol.style.ImageState.LOADING; + this.imageListenerKeys_ = [ + ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, + this.handleImageError_, this), + ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, + this.handleImageLoad_, this) + ]; + try { + this.image_.src = this.src_; + } catch (e) { + this.handleImageError_(); + } + } +}; + + +/** + * @private + */ +ol.style.IconImage_.prototype.replaceColor_ = function() { + if (this.tainting_ || this.color_ === null) { + return; + } + + goog.asserts.assert(this.canvas_ !== null, + 'this.canvas_ must not be null'); + + this.canvas_.width = this.image_.width; + this.canvas_.height = this.image_.height; + + var ctx = this.canvas_.getContext('2d'); + ctx.drawImage(this.image_, 0, 0); + + var imgData = ctx.getImageData(0, 0, this.image_.width, this.image_.height); + var data = imgData.data; + var r = this.color_[0] / 255.0; + var g = this.color_[1] / 255.0; + var b = this.color_[2] / 255.0; + + for (var i = 0, ii = data.length; i < ii; i += 4) { + data[i] *= r; + data[i + 1] *= g; + data[i + 2] *= b; + } + ctx.putImageData(imgData, 0, 0); +}; + + +/** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ +ol.style.IconImage_.prototype.unlistenImage_ = function() { + goog.asserts.assert(this.imageListenerKeys_, + 'we must have listeners registered'); + this.imageListenerKeys_.forEach(ol.events.unlistenByKey); + this.imageListenerKeys_ = null; +}; + + +/** + * @constructor + */ +ol.style.IconImageCache = function() { + + /** + * @type {Object.<string, ol.style.IconImage_>} + * @private + */ + this.cache_ = {}; + + /** + * @type {number} + * @private + */ + this.cacheSize_ = 0; + + /** + * @const + * @type {number} + * @private + */ + this.maxCacheSize_ = 32; +}; +goog.addSingletonGetter(ol.style.IconImageCache); + + +/** + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {ol.Color} color Color. + * @return {string} Cache key. + */ +ol.style.IconImageCache.getKey = function(src, crossOrigin, color) { + goog.asserts.assert(crossOrigin !== undefined, + 'argument crossOrigin must be defined'); + var colorString = color ? ol.color.asString(color) : 'null'; + return crossOrigin + ':' + src + ':' + colorString; +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.style.IconImageCache.prototype.clear = function() { + this.cache_ = {}; + this.cacheSize_ = 0; +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.style.IconImageCache.prototype.expire = function() { + if (this.cacheSize_ > this.maxCacheSize_) { + var i = 0; + var key, iconImage; + for (key in this.cache_) { + iconImage = this.cache_[key]; + if ((i++ & 3) === 0 && !iconImage.hasListener()) { + delete this.cache_[key]; + --this.cacheSize_; + } + } + } +}; + + +/** + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {ol.Color} color Color. + * @return {ol.style.IconImage_} Icon image. + */ +ol.style.IconImageCache.prototype.get = function(src, crossOrigin, color) { + var key = ol.style.IconImageCache.getKey(src, crossOrigin, color); + return key in this.cache_ ? this.cache_[key] : null; +}; + + +/** + * @param {string} src Src. + * @param {?string} crossOrigin Cross origin. + * @param {ol.Color} color Color. + * @param {ol.style.IconImage_} iconImage Icon image. + */ +ol.style.IconImageCache.prototype.set = function(src, crossOrigin, color, + iconImage) { + var key = ol.style.IconImageCache.getKey(src, crossOrigin, color); + this.cache_[key] = iconImage; + ++this.cacheSize_; +}; + +goog.provide('ol.RendererType'); +goog.provide('ol.renderer.Map'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol'); +goog.require('ol.Disposable'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.functions'); +goog.require('ol.layer.Layer'); +goog.require('ol.renderer.Layer'); +goog.require('ol.style.IconImageCache'); +goog.require('ol.vec.Mat4'); + + +/** + * Available renderers: `'canvas'`, `'dom'` or `'webgl'`. + * @enum {string} + */ +ol.RendererType = { + CANVAS: 'canvas', + DOM: 'dom', + WEBGL: 'webgl' +}; + + +/** + * @constructor + * @extends {ol.Disposable} + * @param {Element} container Container. + * @param {ol.Map} map Map. + * @struct + */ +ol.renderer.Map = function(container, map) { + + ol.Disposable.call(this); + + + /** + * @private + * @type {ol.Map} + */ + this.map_ = map; + + /** + * @private + * @type {Object.<string, ol.renderer.Layer>} + */ + this.layerRenderers_ = {}; + + /** + * @private + * @type {Object.<string, ol.EventsKey>} + */ + this.layerRendererListeners_ = {}; + +}; +ol.inherits(ol.renderer.Map, ol.Disposable); + + +/** + * @param {olx.FrameState} frameState FrameState. + * @protected + */ +ol.renderer.Map.prototype.calculateMatrices2D = function(frameState) { + var viewState = frameState.viewState; + var coordinateToPixelMatrix = frameState.coordinateToPixelMatrix; + goog.asserts.assert(coordinateToPixelMatrix, + 'frameState has a coordinateToPixelMatrix'); + ol.vec.Mat4.makeTransform2D(coordinateToPixelMatrix, + frameState.size[0] / 2, frameState.size[1] / 2, + 1 / viewState.resolution, -1 / viewState.resolution, + -viewState.rotation, + -viewState.center[0], -viewState.center[1]); + var inverted = goog.vec.Mat4.invert( + coordinateToPixelMatrix, frameState.pixelToCoordinateMatrix); + goog.asserts.assert(inverted, 'matrix could be inverted'); +}; + + +/** + * @param {ol.layer.Layer} layer Layer. + * @protected + * @return {ol.renderer.Layer} layerRenderer Layer renderer. + */ +ol.renderer.Map.prototype.createLayerRenderer = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.renderer.Map.prototype.disposeInternal = function() { + for (var id in this.layerRenderers_) { + this.layerRenderers_[id].dispose(); + } +}; + + +/** + * @param {ol.Map} map Map. + * @param {olx.FrameState} frameState Frame state. + * @private + */ +ol.renderer.Map.expireIconCache_ = function(map, frameState) { + ol.style.IconImageCache.getInstance().expire(); +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {olx.FrameState} frameState FrameState. + * @param {function(this: S, (ol.Feature|ol.render.Feature), + * ol.layer.Layer): T} callback Feature callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter + * function, only layers which are visible and for which this function + * returns `true` will be tested for features. By default, all visible + * layers will be tested. + * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ +ol.renderer.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg, + layerFilter, thisArg2) { + var result; + var viewState = frameState.viewState; + var viewResolution = viewState.resolution; + + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {ol.layer.Layer} layer Layer. + * @return {?} Callback result. + */ + function forEachFeatureAtCoordinate(feature, layer) { + goog.asserts.assert(feature !== undefined, 'received a feature'); + var key = goog.getUid(feature).toString(); + var managed = frameState.layerStates[goog.getUid(layer)].managed; + if (!(key in frameState.skippedFeatureUids && !managed)) { + return callback.call(thisArg, feature, managed ? layer : null); + } + } + + var projection = viewState.projection; + + var translatedCoordinate = coordinate; + if (projection.canWrapX()) { + var projectionExtent = projection.getExtent(); + var worldWidth = ol.extent.getWidth(projectionExtent); + var x = coordinate[0]; + if (x < projectionExtent[0] || x > projectionExtent[2]) { + var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); + translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]]; + } + } + + var layerStates = frameState.layerStatesArray; + var numLayers = layerStates.length; + var i; + for (i = numLayers - 1; i >= 0; --i) { + var layerState = layerStates[i]; + var layer = layerState.layer; + if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && + layerFilter.call(thisArg2, layer)) { + var layerRenderer = this.getLayerRenderer(layer); + if (layer.getSource()) { + result = layerRenderer.forEachFeatureAtCoordinate( + layer.getSource().getWrapX() ? translatedCoordinate : coordinate, + frameState, forEachFeatureAtCoordinate, thisArg); + } + if (result) { + return result; + } + } + } + return undefined; +}; + + +/** + * @param {ol.Pixel} pixel Pixel. + * @param {olx.FrameState} frameState FrameState. + * @param {function(this: S, ol.layer.Layer): T} callback Layer + * callback. + * @param {S} thisArg Value to use as `this` when executing `callback`. + * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter + * function, only layers which are visible and for which this function + * returns `true` will be tested for features. By default, all visible + * layers will be tested. + * @param {U} thisArg2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result. + * @template S,T,U + */ +ol.renderer.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg, + layerFilter, thisArg2) { + var result; + var viewState = frameState.viewState; + var viewResolution = viewState.resolution; + + var layerStates = frameState.layerStatesArray; + var numLayers = layerStates.length; + var i; + for (i = numLayers - 1; i >= 0; --i) { + var layerState = layerStates[i]; + var layer = layerState.layer; + if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && + layerFilter.call(thisArg2, layer)) { + var layerRenderer = this.getLayerRenderer(layer); + result = layerRenderer.forEachLayerAtPixel( + pixel, frameState, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {olx.FrameState} frameState FrameState. + * @param {function(this: U, ol.layer.Layer): boolean} layerFilter Layer filter + * function, only layers which are visible and for which this function + * returns `true` will be tested for features. By default, all visible + * layers will be tested. + * @param {U} thisArg Value to use as `this` when executing `layerFilter`. + * @return {boolean} Is there a feature at the given coordinate? + * @template U + */ +ol.renderer.Map.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, layerFilter, thisArg) { + var hasFeature = this.forEachFeatureAtCoordinate( + coordinate, frameState, ol.functions.TRUE, this, layerFilter, thisArg); + + return hasFeature !== undefined; +}; + + +/** + * @param {ol.layer.Layer} layer Layer. + * @protected + * @return {ol.renderer.Layer} Layer renderer. + */ +ol.renderer.Map.prototype.getLayerRenderer = function(layer) { + var layerKey = goog.getUid(layer).toString(); + if (layerKey in this.layerRenderers_) { + return this.layerRenderers_[layerKey]; + } else { + var layerRenderer = this.createLayerRenderer(layer); + this.layerRenderers_[layerKey] = layerRenderer; + this.layerRendererListeners_[layerKey] = ol.events.listen(layerRenderer, + ol.events.EventType.CHANGE, this.handleLayerRendererChange_, this); + + return layerRenderer; + } +}; + + +/** + * @param {string} layerKey Layer key. + * @protected + * @return {ol.renderer.Layer} Layer renderer. + */ +ol.renderer.Map.prototype.getLayerRendererByKey = function(layerKey) { + goog.asserts.assert(layerKey in this.layerRenderers_, + 'given layerKey (%s) exists in layerRenderers', layerKey); + return this.layerRenderers_[layerKey]; +}; + + +/** + * @protected + * @return {Object.<string, ol.renderer.Layer>} Layer renderers. + */ +ol.renderer.Map.prototype.getLayerRenderers = function() { + return this.layerRenderers_; +}; + + +/** + * @return {ol.Map} Map. + */ +ol.renderer.Map.prototype.getMap = function() { + return this.map_; +}; + + +/** + * @return {string} Type + */ +ol.renderer.Map.prototype.getType = goog.abstractMethod; + + +/** + * Handle changes in a layer renderer. + * @private + */ +ol.renderer.Map.prototype.handleLayerRendererChange_ = function() { + this.map_.render(); +}; + + +/** + * @param {string} layerKey Layer key. + * @return {ol.renderer.Layer} Layer renderer. + * @private + */ +ol.renderer.Map.prototype.removeLayerRendererByKey_ = function(layerKey) { + goog.asserts.assert(layerKey in this.layerRenderers_, + 'given layerKey (%s) exists in layerRenderers', layerKey); + var layerRenderer = this.layerRenderers_[layerKey]; + delete this.layerRenderers_[layerKey]; + + goog.asserts.assert(layerKey in this.layerRendererListeners_, + 'given layerKey (%s) exists in layerRendererListeners', layerKey); + ol.events.unlistenByKey(this.layerRendererListeners_[layerKey]); + delete this.layerRendererListeners_[layerKey]; + + return layerRenderer; +}; + + +/** + * Render. + * @param {?olx.FrameState} frameState Frame state. + */ +ol.renderer.Map.prototype.renderFrame = ol.nullFunction; + + +/** + * @param {ol.Map} map Map. + * @param {olx.FrameState} frameState Frame state. + * @private + */ +ol.renderer.Map.prototype.removeUnusedLayerRenderers_ = function(map, frameState) { + var layerKey; + for (layerKey in this.layerRenderers_) { + if (!frameState || !(layerKey in frameState.layerStates)) { + this.removeLayerRendererByKey_(layerKey).dispose(); + } + } +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @protected + */ +ol.renderer.Map.prototype.scheduleExpireIconCache = function(frameState) { + frameState.postRenderFunctions.push( + /** @type {ol.PostRenderFunction} */ (ol.renderer.Map.expireIconCache_) + ); +}; + + +/** + * @param {!olx.FrameState} frameState Frame state. + * @protected + */ +ol.renderer.Map.prototype.scheduleRemoveUnusedLayerRenderers = function(frameState) { + var layerKey; + for (layerKey in this.layerRenderers_) { + if (!(layerKey in frameState.layerStates)) { + frameState.postRenderFunctions.push( + /** @type {ol.PostRenderFunction} */ (this.removeUnusedLayerRenderers_.bind(this)) + ); + return; + } + } +}; + + +/** + * @param {ol.LayerState} state1 First layer state. + * @param {ol.LayerState} state2 Second layer state. + * @return {number} The zIndex difference. + */ +ol.renderer.Map.sortByZIndex = function(state1, state2) { + return state1.zIndex - state2.zIndex; +}; + +goog.provide('ol.structs.PriorityQueue'); + +goog.require('goog.asserts'); +goog.require('ol.object'); + + +/** + * Priority queue. + * + * The implementation is inspired from the Closure Library's Heap class and + * Python's heapq module. + * + * @see http://closure-library.googlecode.com/svn/docs/closure_goog_structs_heap.js.source.html + * @see http://hg.python.org/cpython/file/2.7/Lib/heapq.py + * + * @constructor + * @param {function(T): number} priorityFunction Priority function. + * @param {function(T): string} keyFunction Key function. + * @struct + * @template T + */ +ol.structs.PriorityQueue = function(priorityFunction, keyFunction) { + + /** + * @type {function(T): number} + * @private + */ + this.priorityFunction_ = priorityFunction; + + /** + * @type {function(T): string} + * @private + */ + this.keyFunction_ = keyFunction; + + /** + * @type {Array.<T>} + * @private + */ + this.elements_ = []; + + /** + * @type {Array.<number>} + * @private + */ + this.priorities_ = []; + + /** + * @type {Object.<string, boolean>} + * @private + */ + this.queuedElements_ = {}; + +}; + + +/** + * @const + * @type {number} + */ +ol.structs.PriorityQueue.DROP = Infinity; + + +/** + * FIXME empty description for jsdoc + */ +ol.structs.PriorityQueue.prototype.assertValid = function() { + var elements = this.elements_; + var priorities = this.priorities_; + var n = elements.length; + goog.asserts.assert(priorities.length == n); + var i, priority; + for (i = 0; i < (n >> 1) - 1; ++i) { + priority = priorities[i]; + goog.asserts.assert(priority <= priorities[this.getLeftChildIndex_(i)], + 'priority smaller than or equal to priority of left child (%s <= %s)', + priority, priorities[this.getLeftChildIndex_(i)]); + goog.asserts.assert(priority <= priorities[this.getRightChildIndex_(i)], + 'priority smaller than or equal to priority of right child (%s <= %s)', + priority, priorities[this.getRightChildIndex_(i)]); + } +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.structs.PriorityQueue.prototype.clear = function() { + this.elements_.length = 0; + this.priorities_.length = 0; + ol.object.clear(this.queuedElements_); +}; + + +/** + * Remove and return the highest-priority element. O(log N). + * @return {T} Element. + */ +ol.structs.PriorityQueue.prototype.dequeue = function() { + var elements = this.elements_; + goog.asserts.assert(elements.length > 0, + 'must have elements in order to be able to dequeue'); + var priorities = this.priorities_; + var element = elements[0]; + if (elements.length == 1) { + elements.length = 0; + priorities.length = 0; + } else { + elements[0] = elements.pop(); + priorities[0] = priorities.pop(); + this.siftUp_(0); + } + var elementKey = this.keyFunction_(element); + goog.asserts.assert(elementKey in this.queuedElements_, + 'key %s is not listed as queued', elementKey); + delete this.queuedElements_[elementKey]; + return element; +}; + + +/** + * Enqueue an element. O(log N). + * @param {T} element Element. + * @return {boolean} The element was added to the queue. + */ +ol.structs.PriorityQueue.prototype.enqueue = function(element) { + goog.asserts.assert(!(this.keyFunction_(element) in this.queuedElements_), + 'key %s is already listed as queued', this.keyFunction_(element)); + var priority = this.priorityFunction_(element); + if (priority != ol.structs.PriorityQueue.DROP) { + this.elements_.push(element); + this.priorities_.push(priority); + this.queuedElements_[this.keyFunction_(element)] = true; + this.siftDown_(0, this.elements_.length - 1); + return true; + } + return false; +}; + + +/** + * @return {number} Count. + */ +ol.structs.PriorityQueue.prototype.getCount = function() { + return this.elements_.length; +}; + + +/** + * Gets the index of the left child of the node at the given index. + * @param {number} index The index of the node to get the left child for. + * @return {number} The index of the left child. + * @private + */ +ol.structs.PriorityQueue.prototype.getLeftChildIndex_ = function(index) { + return index * 2 + 1; +}; + + +/** + * Gets the index of the right child of the node at the given index. + * @param {number} index The index of the node to get the right child for. + * @return {number} The index of the right child. + * @private + */ +ol.structs.PriorityQueue.prototype.getRightChildIndex_ = function(index) { + return index * 2 + 2; +}; + + +/** + * Gets the index of the parent of the node at the given index. + * @param {number} index The index of the node to get the parent for. + * @return {number} The index of the parent. + * @private + */ +ol.structs.PriorityQueue.prototype.getParentIndex_ = function(index) { + return (index - 1) >> 1; +}; + + +/** + * Make this a heap. O(N). + * @private + */ +ol.structs.PriorityQueue.prototype.heapify_ = function() { + var i; + for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) { + this.siftUp_(i); + } +}; + + +/** + * @return {boolean} Is empty. + */ +ol.structs.PriorityQueue.prototype.isEmpty = function() { + return this.elements_.length === 0; +}; + + +/** + * @param {string} key Key. + * @return {boolean} Is key queued. + */ +ol.structs.PriorityQueue.prototype.isKeyQueued = function(key) { + return key in this.queuedElements_; +}; + + +/** + * @param {T} element Element. + * @return {boolean} Is queued. + */ +ol.structs.PriorityQueue.prototype.isQueued = function(element) { + return this.isKeyQueued(this.keyFunction_(element)); +}; + + +/** + * @param {number} index The index of the node to move down. + * @private + */ +ol.structs.PriorityQueue.prototype.siftUp_ = function(index) { + var elements = this.elements_; + var priorities = this.priorities_; + var count = elements.length; + var element = elements[index]; + var priority = priorities[index]; + var startIndex = index; + + while (index < (count >> 1)) { + var lIndex = this.getLeftChildIndex_(index); + var rIndex = this.getRightChildIndex_(index); + + var smallerChildIndex = rIndex < count && + priorities[rIndex] < priorities[lIndex] ? + rIndex : lIndex; + + elements[index] = elements[smallerChildIndex]; + priorities[index] = priorities[smallerChildIndex]; + index = smallerChildIndex; + } + + elements[index] = element; + priorities[index] = priority; + this.siftDown_(startIndex, index); +}; + + +/** + * @param {number} startIndex The index of the root. + * @param {number} index The index of the node to move up. + * @private + */ +ol.structs.PriorityQueue.prototype.siftDown_ = function(startIndex, index) { + var elements = this.elements_; + var priorities = this.priorities_; + var element = elements[index]; + var priority = priorities[index]; + + while (index > startIndex) { + var parentIndex = this.getParentIndex_(index); + if (priorities[parentIndex] > priority) { + elements[index] = elements[parentIndex]; + priorities[index] = priorities[parentIndex]; + index = parentIndex; + } else { + break; + } + } + elements[index] = element; + priorities[index] = priority; +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.structs.PriorityQueue.prototype.reprioritize = function() { + var priorityFunction = this.priorityFunction_; + var elements = this.elements_; + var priorities = this.priorities_; + var index = 0; + var n = elements.length; + var element, i, priority; + for (i = 0; i < n; ++i) { + element = elements[i]; + priority = priorityFunction(element); + if (priority == ol.structs.PriorityQueue.DROP) { + delete this.queuedElements_[this.keyFunction_(element)]; + } else { + priorities[index] = priority; + elements[index++] = element; + } + } + elements.length = index; + priorities.length = index; + this.heapify_(); +}; + +goog.provide('ol.TileQueue'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.TileState'); +goog.require('ol.structs.PriorityQueue'); + + +/** + * @constructor + * @extends {ol.structs.PriorityQueue.<Array>} + * @param {ol.TilePriorityFunction} tilePriorityFunction + * Tile priority function. + * @param {function(): ?} tileChangeCallback + * Function called on each tile change event. + * @struct + */ +ol.TileQueue = function(tilePriorityFunction, tileChangeCallback) { + + ol.structs.PriorityQueue.call( + this, + /** + * @param {Array} element Element. + * @return {number} Priority. + */ + function(element) { + return tilePriorityFunction.apply(null, element); + }, + /** + * @param {Array} element Element. + * @return {string} Key. + */ + function(element) { + return /** @type {ol.Tile} */ (element[0]).getKey(); + }); + + /** + * @private + * @type {function(): ?} + */ + this.tileChangeCallback_ = tileChangeCallback; + + /** + * @private + * @type {number} + */ + this.tilesLoading_ = 0; + + /** + * @private + * @type {!Object.<string,boolean>} + */ + this.tilesLoadingKeys_ = {}; + +}; +ol.inherits(ol.TileQueue, ol.structs.PriorityQueue); + + +/** + * @inheritDoc + */ +ol.TileQueue.prototype.enqueue = function(element) { + var added = ol.structs.PriorityQueue.prototype.enqueue.call(this, element); + if (added) { + var tile = element[0]; + ol.events.listen(tile, ol.events.EventType.CHANGE, + this.handleTileChange, this); + } + return added; +}; + + +/** + * @return {number} Number of tiles loading. + */ +ol.TileQueue.prototype.getTilesLoading = function() { + return this.tilesLoading_; +}; + + +/** + * @param {ol.events.Event} event Event. + * @protected + */ +ol.TileQueue.prototype.handleTileChange = function(event) { + var tile = /** @type {ol.Tile} */ (event.target); + var state = tile.getState(); + if (state === ol.TileState.LOADED || state === ol.TileState.ERROR || + state === ol.TileState.EMPTY || state === ol.TileState.ABORT) { + ol.events.unlisten(tile, ol.events.EventType.CHANGE, + this.handleTileChange, this); + var tileKey = tile.getKey(); + if (tileKey in this.tilesLoadingKeys_) { + delete this.tilesLoadingKeys_[tileKey]; + --this.tilesLoading_; + } + this.tileChangeCallback_(); + } + goog.asserts.assert(Object.keys(this.tilesLoadingKeys_).length === this.tilesLoading_); +}; + + +/** + * @param {number} maxTotalLoading Maximum number tiles to load simultaneously. + * @param {number} maxNewLoads Maximum number of new tiles to load. + */ +ol.TileQueue.prototype.loadMoreTiles = function(maxTotalLoading, maxNewLoads) { + var newLoads = 0; + var tile, tileKey; + while (this.tilesLoading_ < maxTotalLoading && newLoads < maxNewLoads && + this.getCount() > 0) { + tile = /** @type {ol.Tile} */ (this.dequeue()[0]); + tileKey = tile.getKey(); + if (tile.getState() === ol.TileState.IDLE && !(tileKey in this.tilesLoadingKeys_)) { + this.tilesLoadingKeys_[tileKey] = true; + ++this.tilesLoading_; + ++newLoads; + tile.load(); + } + goog.asserts.assert(Object.keys(this.tilesLoadingKeys_).length === this.tilesLoading_); + } +}; + +goog.provide('ol.Kinetic'); + +goog.require('ol.animation'); + + +/** + * @classdesc + * Implementation of inertial deceleration for map movement. + * + * @constructor + * @param {number} decay Rate of decay (must be negative). + * @param {number} minVelocity Minimum velocity (pixels/millisecond). + * @param {number} delay Delay to consider to calculate the kinetic + * initial values (milliseconds). + * @struct + * @api + */ +ol.Kinetic = function(decay, minVelocity, delay) { + + /** + * @private + * @type {number} + */ + this.decay_ = decay; + + /** + * @private + * @type {number} + */ + this.minVelocity_ = minVelocity; + + /** + * @private + * @type {number} + */ + this.delay_ = delay; + + /** + * @private + * @type {Array.<number>} + */ + this.points_ = []; + + /** + * @private + * @type {number} + */ + this.angle_ = 0; + + /** + * @private + * @type {number} + */ + this.initialVelocity_ = 0; +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.Kinetic.prototype.begin = function() { + this.points_.length = 0; + this.angle_ = 0; + this.initialVelocity_ = 0; +}; + + +/** + * @param {number} x X. + * @param {number} y Y. + */ +ol.Kinetic.prototype.update = function(x, y) { + this.points_.push(x, y, Date.now()); +}; + + +/** + * @return {boolean} Whether we should do kinetic animation. + */ +ol.Kinetic.prototype.end = function() { + if (this.points_.length < 6) { + // at least 2 points are required (i.e. there must be at least 6 elements + // in the array) + return false; + } + var delay = Date.now() - this.delay_; + var lastIndex = this.points_.length - 3; + if (this.points_[lastIndex + 2] < delay) { + // the last tracked point is too old, which means that the user stopped + // panning before releasing the map + return false; + } + + // get the first point which still falls into the delay time + var firstIndex = lastIndex - 3; + while (firstIndex > 0 && this.points_[firstIndex + 2] > delay) { + firstIndex -= 3; + } + var duration = this.points_[lastIndex + 2] - this.points_[firstIndex + 2]; + var dx = this.points_[lastIndex] - this.points_[firstIndex]; + var dy = this.points_[lastIndex + 1] - this.points_[firstIndex + 1]; + this.angle_ = Math.atan2(dy, dx); + this.initialVelocity_ = Math.sqrt(dx * dx + dy * dy) / duration; + return this.initialVelocity_ > this.minVelocity_; +}; + + +/** + * @param {ol.Coordinate} source Source coordinate for the animation. + * @return {ol.PreRenderFunction} Pre-render function for kinetic animation. + */ +ol.Kinetic.prototype.pan = function(source) { + var decay = this.decay_; + var initialVelocity = this.initialVelocity_; + var velocity = this.minVelocity_ - initialVelocity; + var duration = this.getDuration_(); + var easingFunction = ( + /** + * @param {number} t T. + * @return {number} Easing. + */ + function(t) { + return initialVelocity * (Math.exp((decay * t) * duration) - 1) / + velocity; + }); + return ol.animation.pan({ + source: source, + duration: duration, + easing: easingFunction + }); +}; + + +/** + * @private + * @return {number} Duration of animation (milliseconds). + */ +ol.Kinetic.prototype.getDuration_ = function() { + return Math.log(this.minVelocity_ / this.initialVelocity_) / this.decay_; +}; + + +/** + * @return {number} Total distance travelled (pixels). + */ +ol.Kinetic.prototype.getDistance = function() { + return (this.minVelocity_ - this.initialVelocity_) / this.decay_; +}; + + +/** + * @return {number} Angle of the kinetic panning animation (radians). + */ +ol.Kinetic.prototype.getAngle = function() { + return this.angle_; +}; + +// FIXME factor out key precondition (shift et. al) + +goog.provide('ol.interaction.Interaction'); +goog.provide('ol.interaction.InteractionProperty'); + +goog.require('ol'); +goog.require('ol.MapBrowserEvent'); +goog.require('ol.Object'); +goog.require('ol.animation'); +goog.require('ol.easing'); + + +/** + * @enum {string} + */ +ol.interaction.InteractionProperty = { + ACTIVE: 'active' +}; + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * User actions that change the state of the map. Some are similar to controls, + * but are not associated with a DOM element. + * For example, {@link ol.interaction.KeyboardZoom} is functionally the same as + * {@link ol.control.Zoom}, but triggered by a keyboard event not a button + * element event. + * Although interactions do not have a DOM element, some of them do render + * vectors and so are visible on the screen. + * + * @constructor + * @param {olx.interaction.InteractionOptions} options Options. + * @extends {ol.Object} + * @api + */ +ol.interaction.Interaction = function(options) { + + ol.Object.call(this); + + /** + * @private + * @type {ol.Map} + */ + this.map_ = null; + + this.setActive(true); + + /** + * @type {function(ol.MapBrowserEvent):boolean} + */ + this.handleEvent = options.handleEvent; + +}; +ol.inherits(ol.interaction.Interaction, ol.Object); + + +/** + * Return whether the interaction is currently active. + * @return {boolean} `true` if the interaction is active, `false` otherwise. + * @observable + * @api + */ +ol.interaction.Interaction.prototype.getActive = function() { + return /** @type {boolean} */ ( + this.get(ol.interaction.InteractionProperty.ACTIVE)); +}; + + +/** + * Get the map associated with this interaction. + * @return {ol.Map} Map. + * @api + */ +ol.interaction.Interaction.prototype.getMap = function() { + return this.map_; +}; + + +/** + * Activate or deactivate the interaction. + * @param {boolean} active Active. + * @observable + * @api + */ +ol.interaction.Interaction.prototype.setActive = function(active) { + this.set(ol.interaction.InteractionProperty.ACTIVE, active); +}; + + +/** + * Remove the interaction from its current map and attach it to the new map. + * Subclasses may set up event handlers to get notified about changes to + * the map here. + * @param {ol.Map} map Map. + */ +ol.interaction.Interaction.prototype.setMap = function(map) { + this.map_ = map; +}; + + +/** + * @param {ol.Map} map Map. + * @param {ol.View} view View. + * @param {ol.Coordinate} delta Delta. + * @param {number=} opt_duration Duration. + */ +ol.interaction.Interaction.pan = function(map, view, delta, opt_duration) { + var currentCenter = view.getCenter(); + if (currentCenter) { + if (opt_duration && opt_duration > 0) { + map.beforeRender(ol.animation.pan({ + source: currentCenter, + duration: opt_duration, + easing: ol.easing.linear + })); + } + var center = view.constrainCenter( + [currentCenter[0] + delta[0], currentCenter[1] + delta[1]]); + view.setCenter(center); + } +}; + + +/** + * @param {ol.Map} map Map. + * @param {ol.View} view View. + * @param {number|undefined} rotation Rotation. + * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. + */ +ol.interaction.Interaction.rotate = function(map, view, rotation, opt_anchor, opt_duration) { + rotation = view.constrainRotation(rotation, 0); + ol.interaction.Interaction.rotateWithoutConstraints( + map, view, rotation, opt_anchor, opt_duration); +}; + + +/** + * @param {ol.Map} map Map. + * @param {ol.View} view View. + * @param {number|undefined} rotation Rotation. + * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. + */ +ol.interaction.Interaction.rotateWithoutConstraints = function(map, view, rotation, opt_anchor, opt_duration) { + if (rotation !== undefined) { + var currentRotation = view.getRotation(); + var currentCenter = view.getCenter(); + if (currentRotation !== undefined && currentCenter && + opt_duration && opt_duration > 0) { + map.beforeRender(ol.animation.rotate({ + rotation: currentRotation, + duration: opt_duration, + easing: ol.easing.easeOut + })); + if (opt_anchor) { + map.beforeRender(ol.animation.pan({ + source: currentCenter, + duration: opt_duration, + easing: ol.easing.easeOut + })); + } + } + view.rotate(rotation, opt_anchor); + } +}; + + +/** + * @param {ol.Map} map Map. + * @param {ol.View} view View. + * @param {number|undefined} resolution Resolution to go to. + * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. + * @param {number=} opt_direction Zooming direction; > 0 indicates + * zooming out, in which case the constraints system will select + * the largest nearest resolution; < 0 indicates zooming in, in + * which case the constraints system will select the smallest + * nearest resolution; == 0 indicates that the zooming direction + * is unknown/not relevant, in which case the constraints system + * will select the nearest resolution. If not defined 0 is + * assumed. + */ +ol.interaction.Interaction.zoom = function(map, view, resolution, opt_anchor, opt_duration, opt_direction) { + resolution = view.constrainResolution(resolution, 0, opt_direction); + ol.interaction.Interaction.zoomWithoutConstraints( + map, view, resolution, opt_anchor, opt_duration); +}; + + +/** + * @param {ol.Map} map Map. + * @param {ol.View} view View. + * @param {number} delta Delta from previous zoom level. + * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. + */ +ol.interaction.Interaction.zoomByDelta = function(map, view, delta, opt_anchor, opt_duration) { + var currentResolution = view.getResolution(); + var resolution = view.constrainResolution(currentResolution, delta, 0); + ol.interaction.Interaction.zoomWithoutConstraints( + map, view, resolution, opt_anchor, opt_duration); +}; + + +/** + * @param {ol.Map} map Map. + * @param {ol.View} view View. + * @param {number|undefined} resolution Resolution to go to. + * @param {ol.Coordinate=} opt_anchor Anchor coordinate. + * @param {number=} opt_duration Duration. + */ +ol.interaction.Interaction.zoomWithoutConstraints = function(map, view, resolution, opt_anchor, opt_duration) { + if (resolution) { + var currentResolution = view.getResolution(); + var currentCenter = view.getCenter(); + if (currentResolution !== undefined && currentCenter && + resolution !== currentResolution && + opt_duration && opt_duration > 0) { + map.beforeRender(ol.animation.zoom({ + resolution: currentResolution, + duration: opt_duration, + easing: ol.easing.easeOut + })); + if (opt_anchor) { + map.beforeRender(ol.animation.pan({ + source: currentCenter, + duration: opt_duration, + easing: ol.easing.easeOut + })); + } + } + if (opt_anchor) { + var center = view.calculateCenterZoom(resolution, opt_anchor); + view.setCenter(center); + } + view.setResolution(resolution); + } +}; + +goog.provide('ol.interaction.DoubleClickZoom'); + +goog.require('goog.asserts'); +goog.require('ol.MapBrowserEvent'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.interaction.Interaction'); + + +/** + * @classdesc + * Allows the user to zoom by double-clicking on the map. + * + * @constructor + * @extends {ol.interaction.Interaction} + * @param {olx.interaction.DoubleClickZoomOptions=} opt_options Options. + * @api stable + */ +ol.interaction.DoubleClickZoom = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {number} + */ + this.delta_ = options.delta ? options.delta : 1; + + ol.interaction.Interaction.call(this, { + handleEvent: ol.interaction.DoubleClickZoom.handleEvent + }); + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + +}; +ol.inherits(ol.interaction.DoubleClickZoom, ol.interaction.Interaction); + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} (if it was a + * doubleclick) and eventually zooms the map. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.DoubleClickZoom} + * @api + */ +ol.interaction.DoubleClickZoom.handleEvent = function(mapBrowserEvent) { + var stopEvent = false; + var browserEvent = mapBrowserEvent.originalEvent; + if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DBLCLICK) { + var map = mapBrowserEvent.map; + var anchor = mapBrowserEvent.coordinate; + var delta = browserEvent.shiftKey ? -this.delta_ : this.delta_; + var view = map.getView(); + goog.asserts.assert(view, 'map must have a view'); + ol.interaction.Interaction.zoomByDelta( + map, view, delta, anchor, this.duration_); + mapBrowserEvent.preventDefault(); + stopEvent = true; + } + return !stopEvent; +}; + +goog.provide('ol.events.condition'); + +goog.require('goog.asserts'); +goog.require('ol.functions'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.MapBrowserPointerEvent'); + + +/** + * Return `true` if only the alt-key is pressed, `false` otherwise (e.g. when + * additionally the shift-key is pressed). + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if only the alt key is pressed. + * @api stable + */ +ol.events.condition.altKeyOnly = function(mapBrowserEvent) { + var originalEvent = mapBrowserEvent.originalEvent; + return ( + originalEvent.altKey && + !(originalEvent.metaKey || originalEvent.ctrlKey) && + !originalEvent.shiftKey); +}; + + +/** + * Return `true` if only the alt-key and shift-key is pressed, `false` otherwise + * (e.g. when additionally the platform-modifier-key is pressed). + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if only the alt and shift keys are pressed. + * @api stable + */ +ol.events.condition.altShiftKeysOnly = function(mapBrowserEvent) { + var originalEvent = mapBrowserEvent.originalEvent; + return ( + originalEvent.altKey && + !(originalEvent.metaKey || originalEvent.ctrlKey) && + originalEvent.shiftKey); +}; + + +/** + * Return always true. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True. + * @function + * @api stable + */ +ol.events.condition.always = ol.functions.TRUE; + + +/** + * Return `true` if the event is a `click` event, `false` otherwise. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if the event is a map `click` event. + * @api stable + */ +ol.events.condition.click = function(mapBrowserEvent) { + return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.CLICK; +}; + + +/** + * Return `true` if the event has an "action"-producing mouse button. + * + * By definition, this includes left-click on windows/linux, and left-click + * without the ctrl key on Macs. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} The result. + */ +ol.events.condition.mouseActionButton = function(mapBrowserEvent) { + var originalEvent = mapBrowserEvent.originalEvent; + return originalEvent.button == 0 && + !(goog.userAgent.WEBKIT && ol.has.MAC && originalEvent.ctrlKey); +}; + + +/** + * Return always false. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} False. + * @function + * @api stable + */ +ol.events.condition.never = ol.functions.FALSE; + + +/** + * Return `true` if the browser event is a `pointermove` event, `false` + * otherwise. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if the browser event is a `pointermove` event. + * @api + */ +ol.events.condition.pointerMove = function(mapBrowserEvent) { + return mapBrowserEvent.type == 'pointermove'; +}; + + +/** + * Return `true` if the event is a map `singleclick` event, `false` otherwise. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if the event is a map `singleclick` event. + * @api stable + */ +ol.events.condition.singleClick = function(mapBrowserEvent) { + return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.SINGLECLICK; +}; + + +/** + * Return `true` if the event is a map `dblclick` event, `false` otherwise. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if the event is a map `dblclick` event. + * @api stable + */ +ol.events.condition.doubleClick = function(mapBrowserEvent) { + return mapBrowserEvent.type == ol.MapBrowserEvent.EventType.DBLCLICK; +}; + + +/** + * Return `true` if no modifier key (alt-, shift- or platform-modifier-key) is + * pressed. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True only if there no modifier keys are pressed. + * @api stable + */ +ol.events.condition.noModifierKeys = function(mapBrowserEvent) { + var originalEvent = mapBrowserEvent.originalEvent; + return ( + !originalEvent.altKey && + !(originalEvent.metaKey || originalEvent.ctrlKey) && + !originalEvent.shiftKey); +}; + + +/** + * Return `true` if only the platform-modifier-key (the meta-key on Mac, + * ctrl-key otherwise) is pressed, `false` otherwise (e.g. when additionally + * the shift-key is pressed). + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if only the platform modifier key is pressed. + * @api stable + */ +ol.events.condition.platformModifierKeyOnly = function(mapBrowserEvent) { + var originalEvent = mapBrowserEvent.originalEvent; + return ( + !originalEvent.altKey && + (ol.has.MAC ? originalEvent.metaKey : originalEvent.ctrlKey) && + !originalEvent.shiftKey); +}; + + +/** + * Return `true` if only the shift-key is pressed, `false` otherwise (e.g. when + * additionally the alt-key is pressed). + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if only the shift key is pressed. + * @api stable + */ +ol.events.condition.shiftKeyOnly = function(mapBrowserEvent) { + var originalEvent = mapBrowserEvent.originalEvent; + return ( + !originalEvent.altKey && + !(originalEvent.metaKey || originalEvent.ctrlKey) && + originalEvent.shiftKey); +}; + + +/** + * Return `true` if the target element is not editable, i.e. not a `<input>`-, + * `<select>`- or `<textarea>`-element, `false` otherwise. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True only if the target element is not editable. + * @api + */ +ol.events.condition.targetNotEditable = function(mapBrowserEvent) { + var target = mapBrowserEvent.originalEvent.target; + goog.asserts.assertInstanceof(target, Element, + 'target should be an Element'); + var tagName = target.tagName; + return ( + tagName !== 'INPUT' && + tagName !== 'SELECT' && + tagName !== 'TEXTAREA'); +}; + + +/** + * Return `true` if the event originates from a mouse device. + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if the event originates from a mouse device. + * @api stable + */ +ol.events.condition.mouseOnly = function(mapBrowserEvent) { + // see http://www.w3.org/TR/pointerevents/#widl-PointerEvent-pointerType + goog.asserts.assertInstanceof(mapBrowserEvent, ol.MapBrowserPointerEvent, + 'Requires an ol.MapBrowserPointerEvent to work.'); + return mapBrowserEvent.pointerEvent.pointerType == 'mouse'; +}; + + +/** + * Return `true` if the event originates from a primary pointer in + * contact with the surface or if the left mouse button is pressed. + * @see http://www.w3.org/TR/pointerevents/#button-states + * + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} True if the event originates from a primary pointer. + * @api + */ +ol.events.condition.primaryAction = function(mapBrowserEvent) { + var pointerEvent = mapBrowserEvent.pointerEvent; + return pointerEvent.isPrimary && pointerEvent.button === 0; +}; + +goog.provide('ol.interaction.Pointer'); + +goog.require('ol'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.MapBrowserPointerEvent'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.object'); + + +/** + * @classdesc + * Base class that calls user-defined functions on `down`, `move` and `up` + * events. This class also manages "drag sequences". + * + * When the `handleDownEvent` user function returns `true` a drag sequence is + * started. During a drag sequence the `handleDragEvent` user function is + * called on `move` events. The drag sequence ends when the `handleUpEvent` + * user function is called and returns `false`. + * + * @constructor + * @param {olx.interaction.PointerOptions=} opt_options Options. + * @extends {ol.interaction.Interaction} + * @api + */ +ol.interaction.Pointer = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + var handleEvent = options.handleEvent ? + options.handleEvent : ol.interaction.Pointer.handleEvent; + + ol.interaction.Interaction.call(this, { + handleEvent: handleEvent + }); + + /** + * @type {function(ol.MapBrowserPointerEvent):boolean} + * @private + */ + this.handleDownEvent_ = options.handleDownEvent ? + options.handleDownEvent : ol.interaction.Pointer.handleDownEvent; + + /** + * @type {function(ol.MapBrowserPointerEvent)} + * @private + */ + this.handleDragEvent_ = options.handleDragEvent ? + options.handleDragEvent : ol.interaction.Pointer.handleDragEvent; + + /** + * @type {function(ol.MapBrowserPointerEvent)} + * @private + */ + this.handleMoveEvent_ = options.handleMoveEvent ? + options.handleMoveEvent : ol.interaction.Pointer.handleMoveEvent; + + /** + * @type {function(ol.MapBrowserPointerEvent):boolean} + * @private + */ + this.handleUpEvent_ = options.handleUpEvent ? + options.handleUpEvent : ol.interaction.Pointer.handleUpEvent; + + /** + * @type {boolean} + * @protected + */ + this.handlingDownUpSequence = false; + + /** + * @type {Object.<number, ol.pointer.PointerEvent>} + * @private + */ + this.trackedPointers_ = {}; + + /** + * @type {Array.<ol.pointer.PointerEvent>} + * @protected + */ + this.targetPointers = []; + +}; +ol.inherits(ol.interaction.Pointer, ol.interaction.Interaction); + + +/** + * @param {Array.<ol.pointer.PointerEvent>} pointerEvents List of events. + * @return {ol.Pixel} Centroid pixel. + */ +ol.interaction.Pointer.centroid = function(pointerEvents) { + var length = pointerEvents.length; + var clientX = 0; + var clientY = 0; + for (var i = 0; i < length; i++) { + clientX += pointerEvents[i].clientX; + clientY += pointerEvents[i].clientY; + } + return [clientX / length, clientY / length]; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Whether the event is a pointerdown, pointerdrag + * or pointerup event. + * @private + */ +ol.interaction.Pointer.prototype.isPointerDraggingEvent_ = function(mapBrowserEvent) { + var type = mapBrowserEvent.type; + return ( + type === ol.MapBrowserEvent.EventType.POINTERDOWN || + type === ol.MapBrowserEvent.EventType.POINTERDRAG || + type === ol.MapBrowserEvent.EventType.POINTERUP); +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @private + */ +ol.interaction.Pointer.prototype.updateTrackedPointers_ = function(mapBrowserEvent) { + if (this.isPointerDraggingEvent_(mapBrowserEvent)) { + var event = mapBrowserEvent.pointerEvent; + + if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERUP) { + delete this.trackedPointers_[event.pointerId]; + } else if (mapBrowserEvent.type == + ol.MapBrowserEvent.EventType.POINTERDOWN) { + this.trackedPointers_[event.pointerId] = event; + } else if (event.pointerId in this.trackedPointers_) { + // update only when there was a pointerdown event for this pointer + this.trackedPointers_[event.pointerId] = event; + } + this.targetPointers = ol.object.getValues(this.trackedPointers_); + } +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.Pointer} + */ +ol.interaction.Pointer.handleDragEvent = ol.nullFunction; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Capture dragging. + * @this {ol.interaction.Pointer} + */ +ol.interaction.Pointer.handleUpEvent = ol.functions.FALSE; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Capture dragging. + * @this {ol.interaction.Pointer} + */ +ol.interaction.Pointer.handleDownEvent = ol.functions.FALSE; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.Pointer} + */ +ol.interaction.Pointer.handleMoveEvent = ol.nullFunction; + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} and may call into + * other functions, if event sequences like e.g. 'drag' or 'down-up' etc. are + * detected. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.Pointer} + * @api + */ +ol.interaction.Pointer.handleEvent = function(mapBrowserEvent) { + if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) { + return true; + } + + var stopEvent = false; + this.updateTrackedPointers_(mapBrowserEvent); + if (this.handlingDownUpSequence) { + if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERDRAG) { + this.handleDragEvent_(mapBrowserEvent); + } else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERUP) { + this.handlingDownUpSequence = this.handleUpEvent_(mapBrowserEvent); + } + } + if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERDOWN) { + var handled = this.handleDownEvent_(mapBrowserEvent); + this.handlingDownUpSequence = handled; + stopEvent = this.shouldStopEvent(handled); + } else if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE) { + this.handleMoveEvent_(mapBrowserEvent); + } + return !stopEvent; +}; + + +/** + * This method is used to determine if "down" events should be propagated to + * other interactions or should be stopped. + * + * The method receives the return code of the "handleDownEvent" function. + * + * By default this function is the "identity" function. It's overidden in + * child classes. + * + * @param {boolean} handled Was the event handled by the interaction? + * @return {boolean} Should the event be stopped? + * @protected + */ +ol.interaction.Pointer.prototype.shouldStopEvent = function(handled) { + return handled; +}; + +goog.provide('ol.interaction.DragPan'); + +goog.require('goog.asserts'); +goog.require('ol.Kinetic'); + +goog.require('ol.ViewHint'); +goog.require('ol.coordinate'); +goog.require('ol.functions'); +goog.require('ol.events.condition'); +goog.require('ol.interaction.Pointer'); + + +/** + * @classdesc + * Allows the user to pan the map by dragging the map. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.DragPanOptions=} opt_options Options. + * @api stable + */ +ol.interaction.DragPan = function(opt_options) { + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.DragPan.handleDownEvent_, + handleDragEvent: ol.interaction.DragPan.handleDragEvent_, + handleUpEvent: ol.interaction.DragPan.handleUpEvent_ + }); + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {ol.Kinetic|undefined} + */ + this.kinetic_ = options.kinetic; + + /** + * @private + * @type {?ol.PreRenderFunction} + */ + this.kineticPreRenderFn_ = null; + + /** + * @type {ol.Pixel} + */ + this.lastCentroid = null; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? + options.condition : ol.events.condition.noModifierKeys; + + /** + * @private + * @type {boolean} + */ + this.noKinetic_ = false; + +}; +ol.inherits(ol.interaction.DragPan, ol.interaction.Pointer); + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.DragPan} + * @private + */ +ol.interaction.DragPan.handleDragEvent_ = function(mapBrowserEvent) { + goog.asserts.assert(this.targetPointers.length >= 1, + 'the length of this.targetPointers should be more than 1'); + var centroid = + ol.interaction.Pointer.centroid(this.targetPointers); + if (this.kinetic_) { + this.kinetic_.update(centroid[0], centroid[1]); + } + if (this.lastCentroid) { + var deltaX = this.lastCentroid[0] - centroid[0]; + var deltaY = centroid[1] - this.lastCentroid[1]; + var map = mapBrowserEvent.map; + var view = map.getView(); + var viewState = view.getState(); + var center = [deltaX, deltaY]; + ol.coordinate.scale(center, viewState.resolution); + ol.coordinate.rotate(center, viewState.rotation); + ol.coordinate.add(center, viewState.center); + center = view.constrainCenter(center); + map.render(); + view.setCenter(center); + } + this.lastCentroid = centroid; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.DragPan} + * @private + */ +ol.interaction.DragPan.handleUpEvent_ = function(mapBrowserEvent) { + var map = mapBrowserEvent.map; + var view = map.getView(); + if (this.targetPointers.length === 0) { + if (!this.noKinetic_ && this.kinetic_ && this.kinetic_.end()) { + var distance = this.kinetic_.getDistance(); + var angle = this.kinetic_.getAngle(); + var center = view.getCenter(); + goog.asserts.assert(center !== undefined, 'center should be defined'); + this.kineticPreRenderFn_ = this.kinetic_.pan(center); + map.beforeRender(this.kineticPreRenderFn_); + var centerpx = map.getPixelFromCoordinate(center); + var dest = map.getCoordinateFromPixel([ + centerpx[0] - distance * Math.cos(angle), + centerpx[1] - distance * Math.sin(angle) + ]); + dest = view.constrainCenter(dest); + view.setCenter(dest); + } + view.setHint(ol.ViewHint.INTERACTING, -1); + map.render(); + return false; + } else { + this.lastCentroid = null; + return true; + } +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.DragPan} + * @private + */ +ol.interaction.DragPan.handleDownEvent_ = function(mapBrowserEvent) { + if (this.targetPointers.length > 0 && this.condition_(mapBrowserEvent)) { + var map = mapBrowserEvent.map; + var view = map.getView(); + this.lastCentroid = null; + if (!this.handlingDownUpSequence) { + view.setHint(ol.ViewHint.INTERACTING, 1); + } + map.render(); + if (this.kineticPreRenderFn_ && + map.removePreRenderFunction(this.kineticPreRenderFn_)) { + view.setCenter(mapBrowserEvent.frameState.viewState.center); + this.kineticPreRenderFn_ = null; + } + if (this.kinetic_) { + this.kinetic_.begin(); + } + // No kinetic as soon as more than one pointer on the screen is + // detected. This is to prevent nasty pans after pinch. + this.noKinetic_ = this.targetPointers.length > 1; + return true; + } else { + return false; + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.DragPan.prototype.shouldStopEvent = ol.functions.FALSE; + +goog.provide('ol.interaction.DragRotate'); + +goog.require('ol'); +goog.require('ol.ViewHint'); +goog.require('ol.functions'); +goog.require('ol.events.condition'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.interaction.Pointer'); + + +/** + * @classdesc + * Allows the user to rotate the map by clicking and dragging on the map, + * normally combined with an {@link ol.events.condition} that limits + * it to when the alt and shift keys are held down. + * + * This interaction is only supported for mouse devices. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.DragRotateOptions=} opt_options Options. + * @api stable + */ +ol.interaction.DragRotate = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.DragRotate.handleDownEvent_, + handleDragEvent: ol.interaction.DragRotate.handleDragEvent_, + handleUpEvent: ol.interaction.DragRotate.handleUpEvent_ + }); + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? + options.condition : ol.events.condition.altShiftKeysOnly; + + /** + * @private + * @type {number|undefined} + */ + this.lastAngle_ = undefined; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; +}; +ol.inherits(ol.interaction.DragRotate, ol.interaction.Pointer); + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.DragRotate} + * @private + */ +ol.interaction.DragRotate.handleDragEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return; + } + + var map = mapBrowserEvent.map; + var size = map.getSize(); + var offset = mapBrowserEvent.pixel; + var theta = + Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2); + if (this.lastAngle_ !== undefined) { + var delta = theta - this.lastAngle_; + var view = map.getView(); + var rotation = view.getRotation(); + map.render(); + ol.interaction.Interaction.rotateWithoutConstraints( + map, view, rotation - delta); + } + this.lastAngle_ = theta; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.DragRotate} + * @private + */ +ol.interaction.DragRotate.handleUpEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return true; + } + + var map = mapBrowserEvent.map; + var view = map.getView(); + view.setHint(ol.ViewHint.INTERACTING, -1); + var rotation = view.getRotation(); + ol.interaction.Interaction.rotate(map, view, rotation, + undefined, this.duration_); + return false; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.DragRotate} + * @private + */ +ol.interaction.DragRotate.handleDownEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return false; + } + + if (ol.events.condition.mouseActionButton(mapBrowserEvent) && + this.condition_(mapBrowserEvent)) { + var map = mapBrowserEvent.map; + map.getView().setHint(ol.ViewHint.INTERACTING, 1); + map.render(); + this.lastAngle_ = undefined; + return true; + } else { + return false; + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.DragRotate.prototype.shouldStopEvent = ol.functions.FALSE; + +// FIXME add rotation + +goog.provide('ol.render.Box'); + +goog.require('goog.asserts'); +goog.require('ol.Disposable'); +goog.require('ol.geom.Polygon'); + + +/** + * @constructor + * @extends {ol.Disposable} + * @param {string} className CSS class name. + */ +ol.render.Box = function(className) { + + /** + * @type {ol.geom.Polygon} + * @private + */ + this.geometry_ = null; + + /** + * @type {HTMLDivElement} + * @private + */ + this.element_ = /** @type {HTMLDivElement} */ (document.createElement('div')); + this.element_.style.position = 'absolute'; + this.element_.className = 'ol-box ' + className; + + /** + * @private + * @type {ol.Map} + */ + this.map_ = null; + + /** + * @private + * @type {ol.Pixel} + */ + this.startPixel_ = null; + + /** + * @private + * @type {ol.Pixel} + */ + this.endPixel_ = null; + +}; +ol.inherits(ol.render.Box, ol.Disposable); + + +/** + * @inheritDoc + */ +ol.render.Box.prototype.disposeInternal = function() { + this.setMap(null); +}; + + +/** + * @private + */ +ol.render.Box.prototype.render_ = function() { + var startPixel = this.startPixel_; + var endPixel = this.endPixel_; + goog.asserts.assert(startPixel, 'this.startPixel_ must be truthy'); + goog.asserts.assert(endPixel, 'this.endPixel_ must be truthy'); + var px = 'px'; + var style = this.element_.style; + style.left = Math.min(startPixel[0], endPixel[0]) + px; + style.top = Math.min(startPixel[1], endPixel[1]) + px; + style.width = Math.abs(endPixel[0] - startPixel[0]) + px; + style.height = Math.abs(endPixel[1] - startPixel[1]) + px; +}; + + +/** + * @param {ol.Map} map Map. + */ +ol.render.Box.prototype.setMap = function(map) { + if (this.map_) { + this.map_.getOverlayContainer().removeChild(this.element_); + var style = this.element_.style; + style.left = style.top = style.width = style.height = 'inherit'; + } + this.map_ = map; + if (this.map_) { + this.map_.getOverlayContainer().appendChild(this.element_); + } +}; + + +/** + * @param {ol.Pixel} startPixel Start pixel. + * @param {ol.Pixel} endPixel End pixel. + */ +ol.render.Box.prototype.setPixels = function(startPixel, endPixel) { + this.startPixel_ = startPixel; + this.endPixel_ = endPixel; + this.createOrUpdateGeometry(); + this.render_(); +}; + + +/** + * Creates or updates the cached geometry. + */ +ol.render.Box.prototype.createOrUpdateGeometry = function() { + goog.asserts.assert(this.startPixel_, + 'this.startPixel_ must be truthy'); + goog.asserts.assert(this.endPixel_, + 'this.endPixel_ must be truthy'); + goog.asserts.assert(this.map_, 'this.map_ must be truthy'); + var startPixel = this.startPixel_; + var endPixel = this.endPixel_; + var pixels = [ + startPixel, + [startPixel[0], endPixel[1]], + endPixel, + [endPixel[0], startPixel[1]] + ]; + var coordinates = pixels.map(this.map_.getCoordinateFromPixel, this.map_); + // close the polygon + coordinates[4] = coordinates[0].slice(); + if (!this.geometry_) { + this.geometry_ = new ol.geom.Polygon([coordinates]); + } else { + this.geometry_.setCoordinates([coordinates]); + } +}; + + +/** + * @return {ol.geom.Polygon} Geometry. + */ +ol.render.Box.prototype.getGeometry = function() { + return this.geometry_; +}; + +// FIXME draw drag box +goog.provide('ol.DragBoxEvent'); +goog.provide('ol.interaction.DragBox'); + +goog.require('ol.events.Event'); +goog.require('ol'); +goog.require('ol.events.condition'); +goog.require('ol.interaction.Pointer'); +goog.require('ol.render.Box'); + + +/** + * @const + * @type {number} + */ +ol.DRAG_BOX_HYSTERESIS_PIXELS_SQUARED = + ol.DRAG_BOX_HYSTERESIS_PIXELS * + ol.DRAG_BOX_HYSTERESIS_PIXELS; + + +/** + * @enum {string} + */ +ol.DragBoxEventType = { + /** + * Triggered upon drag box start. + * @event ol.DragBoxEvent#boxstart + * @api stable + */ + BOXSTART: 'boxstart', + + /** + * Triggered on drag when box is active. + * @event ol.DragBoxEvent#boxdrag + * @api + */ + BOXDRAG: 'boxdrag', + + /** + * Triggered upon drag box end. + * @event ol.DragBoxEvent#boxend + * @api stable + */ + BOXEND: 'boxend' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.interaction.DragBox} instances are instances of + * this type. + * + * @param {string} type The event type. + * @param {ol.Coordinate} coordinate The event coordinate. + * @param {ol.MapBrowserEvent} mapBrowserEvent Originating event. + * @extends {ol.events.Event} + * @constructor + * @implements {oli.DragBoxEvent} + */ +ol.DragBoxEvent = function(type, coordinate, mapBrowserEvent) { + ol.events.Event.call(this, type); + + /** + * The coordinate of the drag event. + * @const + * @type {ol.Coordinate} + * @api stable + */ + this.coordinate = coordinate; + + /** + * @const + * @type {ol.MapBrowserEvent} + * @api + */ + this.mapBrowserEvent = mapBrowserEvent; + +}; +ol.inherits(ol.DragBoxEvent, ol.events.Event); + + +/** + * @classdesc + * Allows the user to draw a vector box by clicking and dragging on the map, + * normally combined with an {@link ol.events.condition} that limits + * it to when the shift or other key is held down. This is used, for example, + * for zooming to a specific area of the map + * (see {@link ol.interaction.DragZoom} and + * {@link ol.interaction.DragRotateAndZoom}). + * + * This interaction is only supported for mouse devices. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @fires ol.DragBoxEvent + * @param {olx.interaction.DragBoxOptions=} opt_options Options. + * @api stable + */ +ol.interaction.DragBox = function(opt_options) { + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.DragBox.handleDownEvent_, + handleDragEvent: ol.interaction.DragBox.handleDragEvent_, + handleUpEvent: ol.interaction.DragBox.handleUpEvent_ + }); + + var options = opt_options ? opt_options : {}; + + /** + * @type {ol.render.Box} + * @private + */ + this.box_ = new ol.render.Box(options.className || 'ol-dragbox'); + + /** + * @type {ol.Pixel} + * @private + */ + this.startPixel_ = null; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? + options.condition : ol.events.condition.always; + + /** + * @private + * @type {ol.DragBoxEndConditionType} + */ + this.boxEndCondition_ = options.boxEndCondition ? + options.boxEndCondition : ol.interaction.DragBox.defaultBoxEndCondition; +}; +ol.inherits(ol.interaction.DragBox, ol.interaction.Pointer); + + +/** + * The default condition for determining whether the boxend event + * should fire. + * @param {ol.MapBrowserEvent} mapBrowserEvent The originating MapBrowserEvent + * leading to the box end. + * @param {ol.Pixel} startPixel The starting pixel of the box. + * @param {ol.Pixel} endPixel The end pixel of the box. + * @return {boolean} Whether or not the boxend condition should be fired. + */ +ol.interaction.DragBox.defaultBoxEndCondition = function(mapBrowserEvent, + startPixel, endPixel) { + var width = endPixel[0] - startPixel[0]; + var height = endPixel[1] - startPixel[1]; + return width * width + height * height >= + ol.DRAG_BOX_HYSTERESIS_PIXELS_SQUARED; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.DragBox} + * @private + */ +ol.interaction.DragBox.handleDragEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return; + } + + this.box_.setPixels(this.startPixel_, mapBrowserEvent.pixel); + + this.dispatchEvent(new ol.DragBoxEvent(ol.DragBoxEventType.BOXDRAG, + mapBrowserEvent.coordinate, mapBrowserEvent)); +}; + + +/** + * Returns geometry of last drawn box. + * @return {ol.geom.Polygon} Geometry. + * @api stable + */ +ol.interaction.DragBox.prototype.getGeometry = function() { + return this.box_.getGeometry(); +}; + + +/** + * To be overriden by child classes. + * FIXME: use constructor option instead of relying on overridding. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @protected + */ +ol.interaction.DragBox.prototype.onBoxEnd = ol.nullFunction; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.DragBox} + * @private + */ +ol.interaction.DragBox.handleUpEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return true; + } + + this.box_.setMap(null); + + if (this.boxEndCondition_(mapBrowserEvent, + this.startPixel_, mapBrowserEvent.pixel)) { + this.onBoxEnd(mapBrowserEvent); + this.dispatchEvent(new ol.DragBoxEvent(ol.DragBoxEventType.BOXEND, + mapBrowserEvent.coordinate, mapBrowserEvent)); + } + return false; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.DragBox} + * @private + */ +ol.interaction.DragBox.handleDownEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return false; + } + + if (ol.events.condition.mouseActionButton(mapBrowserEvent) && + this.condition_(mapBrowserEvent)) { + this.startPixel_ = mapBrowserEvent.pixel; + this.box_.setMap(mapBrowserEvent.map); + this.box_.setPixels(this.startPixel_, this.startPixel_); + this.dispatchEvent(new ol.DragBoxEvent(ol.DragBoxEventType.BOXSTART, + mapBrowserEvent.coordinate, mapBrowserEvent)); + return true; + } else { + return false; + } +}; + +goog.provide('ol.interaction.DragZoom'); + +goog.require('goog.asserts'); +goog.require('ol.animation'); +goog.require('ol.easing'); +goog.require('ol.events.condition'); +goog.require('ol.extent'); +goog.require('ol.interaction.DragBox'); + + +/** + * @classdesc + * Allows the user to zoom the map by clicking and dragging on the map, + * normally combined with an {@link ol.events.condition} that limits + * it to when a key, shift by default, is held down. + * + * To change the style of the box, use CSS and the `.ol-dragzoom` selector, or + * your custom one configured with `className`. + * + * @constructor + * @extends {ol.interaction.DragBox} + * @param {olx.interaction.DragZoomOptions=} opt_options Options. + * @api stable + */ +ol.interaction.DragZoom = function(opt_options) { + var options = opt_options ? opt_options : {}; + + var condition = options.condition ? + options.condition : ol.events.condition.shiftKeyOnly; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 200; + + /** + * @private + * @type {boolean} + */ + this.out_ = options.out !== undefined ? options.out : false; + + ol.interaction.DragBox.call(this, { + condition: condition, + className: options.className || 'ol-dragzoom' + }); + +}; +ol.inherits(ol.interaction.DragZoom, ol.interaction.DragBox); + + +/** + * @inheritDoc + */ +ol.interaction.DragZoom.prototype.onBoxEnd = function() { + var map = this.getMap(); + + var view = map.getView(); + goog.asserts.assert(view, 'map must have view'); + + var size = map.getSize(); + goog.asserts.assert(size !== undefined, 'size should be defined'); + + var extent = this.getGeometry().getExtent(); + + if (this.out_) { + var mapExtent = view.calculateExtent(size); + var boxPixelExtent = ol.extent.createOrUpdateFromCoordinates([ + map.getPixelFromCoordinate(ol.extent.getBottomLeft(extent)), + map.getPixelFromCoordinate(ol.extent.getTopRight(extent))]); + var factor = view.getResolutionForExtent(boxPixelExtent, size); + + ol.extent.scaleFromCenter(mapExtent, 1 / factor); + extent = mapExtent; + } + + var resolution = view.constrainResolution( + view.getResolutionForExtent(extent, size)); + + var currentResolution = view.getResolution(); + goog.asserts.assert(currentResolution !== undefined, 'res should be defined'); + + var currentCenter = view.getCenter(); + goog.asserts.assert(currentCenter !== undefined, 'center should be defined'); + + map.beforeRender(ol.animation.zoom({ + resolution: currentResolution, + duration: this.duration_, + easing: ol.easing.easeOut + })); + map.beforeRender(ol.animation.pan({ + source: currentCenter, + duration: this.duration_, + easing: ol.easing.easeOut + })); + + view.setCenter(ol.extent.getCenter(extent)); + view.setResolution(resolution); +}; + +goog.provide('ol.interaction.KeyboardPan'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.coordinate'); +goog.require('ol.events.EventType'); +goog.require('ol.events.KeyCode'); +goog.require('ol.events.condition'); +goog.require('ol.interaction.Interaction'); + + +/** + * @classdesc + * Allows the user to pan the map using keyboard arrows. + * Note that, although this interaction is by default included in maps, + * the keys can only be used when browser focus is on the element to which + * the keyboard events are attached. By default, this is the map div, + * though you can change this with the `keyboardEventTarget` in + * {@link ol.Map}. `document` never loses focus but, for any other element, + * focus will have to be on, and returned to, this element if the keys are to + * function. + * See also {@link ol.interaction.KeyboardZoom}. + * + * @constructor + * @extends {ol.interaction.Interaction} + * @param {olx.interaction.KeyboardPanOptions=} opt_options Options. + * @api stable + */ +ol.interaction.KeyboardPan = function(opt_options) { + + ol.interaction.Interaction.call(this, { + handleEvent: ol.interaction.KeyboardPan.handleEvent + }); + + var options = opt_options || {}; + + /** + * @private + * @param {ol.MapBrowserEvent} mapBrowserEvent Browser event. + * @return {boolean} Combined condition result. + */ + this.defaultCondition_ = function(mapBrowserEvent) { + return ol.events.condition.noModifierKeys(mapBrowserEvent) && + ol.events.condition.targetNotEditable(mapBrowserEvent); + }; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition !== undefined ? + options.condition : this.defaultCondition_; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 100; + + /** + * @private + * @type {number} + */ + this.pixelDelta_ = options.pixelDelta !== undefined ? + options.pixelDelta : 128; + +}; +ol.inherits(ol.interaction.KeyboardPan, ol.interaction.Interaction); + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} if it was a + * `KeyEvent`, and decides the direction to pan to (if an arrow key was + * pressed). + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.KeyboardPan} + * @api + */ +ol.interaction.KeyboardPan.handleEvent = function(mapBrowserEvent) { + var stopEvent = false; + if (mapBrowserEvent.type == ol.events.EventType.KEYDOWN) { + var keyEvent = mapBrowserEvent.originalEvent; + var keyCode = keyEvent.keyCode; + if (this.condition_(mapBrowserEvent) && + (keyCode == ol.events.KeyCode.DOWN || + keyCode == ol.events.KeyCode.LEFT || + keyCode == ol.events.KeyCode.RIGHT || + keyCode == ol.events.KeyCode.UP)) { + var map = mapBrowserEvent.map; + var view = map.getView(); + goog.asserts.assert(view, 'map must have view'); + var mapUnitsDelta = view.getResolution() * this.pixelDelta_; + var deltaX = 0, deltaY = 0; + if (keyCode == ol.events.KeyCode.DOWN) { + deltaY = -mapUnitsDelta; + } else if (keyCode == ol.events.KeyCode.LEFT) { + deltaX = -mapUnitsDelta; + } else if (keyCode == ol.events.KeyCode.RIGHT) { + deltaX = mapUnitsDelta; + } else { + deltaY = mapUnitsDelta; + } + var delta = [deltaX, deltaY]; + ol.coordinate.rotate(delta, view.getRotation()); + ol.interaction.Interaction.pan(map, view, delta, this.duration_); + mapBrowserEvent.preventDefault(); + stopEvent = true; + } + } + return !stopEvent; +}; + +goog.provide('ol.interaction.KeyboardZoom'); + +goog.require('goog.asserts'); +goog.require('ol.events.EventType'); +goog.require('ol.events.condition'); +goog.require('ol.interaction.Interaction'); + + +/** + * @classdesc + * Allows the user to zoom the map using keyboard + and -. + * Note that, although this interaction is by default included in maps, + * the keys can only be used when browser focus is on the element to which + * the keyboard events are attached. By default, this is the map div, + * though you can change this with the `keyboardEventTarget` in + * {@link ol.Map}. `document` never loses focus but, for any other element, + * focus will have to be on, and returned to, this element if the keys are to + * function. + * See also {@link ol.interaction.KeyboardPan}. + * + * @constructor + * @param {olx.interaction.KeyboardZoomOptions=} opt_options Options. + * @extends {ol.interaction.Interaction} + * @api stable + */ +ol.interaction.KeyboardZoom = function(opt_options) { + + ol.interaction.Interaction.call(this, { + handleEvent: ol.interaction.KeyboardZoom.handleEvent + }); + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? options.condition : + ol.events.condition.targetNotEditable; + + /** + * @private + * @type {number} + */ + this.delta_ = options.delta ? options.delta : 1; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 100; + +}; +ol.inherits(ol.interaction.KeyboardZoom, ol.interaction.Interaction); + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} if it was a + * `KeyEvent`, and decides whether to zoom in or out (depending on whether the + * key pressed was '+' or '-'). + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.KeyboardZoom} + * @api + */ +ol.interaction.KeyboardZoom.handleEvent = function(mapBrowserEvent) { + var stopEvent = false; + if (mapBrowserEvent.type == ol.events.EventType.KEYDOWN || + mapBrowserEvent.type == ol.events.EventType.KEYPRESS) { + var keyEvent = mapBrowserEvent.originalEvent; + var charCode = keyEvent.charCode; + if (this.condition_(mapBrowserEvent) && + (charCode == '+'.charCodeAt(0) || charCode == '-'.charCodeAt(0))) { + var map = mapBrowserEvent.map; + var delta = (charCode == '+'.charCodeAt(0)) ? this.delta_ : -this.delta_; + map.render(); + var view = map.getView(); + goog.asserts.assert(view, 'map must have view'); + ol.interaction.Interaction.zoomByDelta( + map, view, delta, undefined, this.duration_); + mapBrowserEvent.preventDefault(); + stopEvent = true; + } + } + return !stopEvent; +}; + +goog.provide('ol.interaction.MouseWheelZoom'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.events.EventType'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.math'); + + +/** + * @classdesc + * Allows the user to zoom the map by scrolling the mouse wheel. + * + * @constructor + * @extends {ol.interaction.Interaction} + * @param {olx.interaction.MouseWheelZoomOptions=} opt_options Options. + * @api stable + */ +ol.interaction.MouseWheelZoom = function(opt_options) { + + ol.interaction.Interaction.call(this, { + handleEvent: ol.interaction.MouseWheelZoom.handleEvent + }); + + var options = opt_options || {}; + + /** + * @private + * @type {number} + */ + this.delta_ = 0; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + + /** + * @private + * @type {boolean} + */ + this.useAnchor_ = options.useAnchor !== undefined ? options.useAnchor : true; + + /** + * @private + * @type {?ol.Coordinate} + */ + this.lastAnchor_ = null; + + /** + * @private + * @type {number|undefined} + */ + this.startTime_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.timeoutId_ = undefined; + +}; +ol.inherits(ol.interaction.MouseWheelZoom, ol.interaction.Interaction); + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} (if it was a + * mousewheel-event) and eventually zooms the map. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.MouseWheelZoom} + * @api + */ +ol.interaction.MouseWheelZoom.handleEvent = function(mapBrowserEvent) { + var stopEvent = false; + if (mapBrowserEvent.type == ol.events.EventType.WHEEL || + mapBrowserEvent.type == ol.events.EventType.MOUSEWHEEL) { + var map = mapBrowserEvent.map; + var wheelEvent = /** @type {WheelEvent} */ (mapBrowserEvent.originalEvent); + + if (this.useAnchor_) { + this.lastAnchor_ = mapBrowserEvent.coordinate; + } + + // Delta normalisation inspired by + // https://github.com/mapbox/mapbox-gl-js/blob/001c7b9/js/ui/handler/scroll_zoom.js + //TODO There's more good stuff in there for inspiration to improve this interaction. + var delta; + if (mapBrowserEvent.type == ol.events.EventType.WHEEL) { + delta = wheelEvent.deltaY; + if (ol.has.FIREFOX && + wheelEvent.deltaMode === ol.global.WheelEvent.DOM_DELTA_PIXEL) { + delta /= ol.has.DEVICE_PIXEL_RATIO; + } + if (wheelEvent.deltaMode === ol.global.WheelEvent.DOM_DELTA_LINE) { + delta *= 40; + } + } else if (mapBrowserEvent.type == ol.events.EventType.MOUSEWHEEL) { + delta = -wheelEvent.wheelDeltaY; + if (ol.has.SAFARI) { + delta /= 3; + } + } + + this.delta_ += delta; + + if (this.startTime_ === undefined) { + this.startTime_ = Date.now(); + } + + var duration = ol.MOUSEWHEELZOOM_TIMEOUT_DURATION; + var timeLeft = Math.max(duration - (Date.now() - this.startTime_), 0); + + ol.global.clearTimeout(this.timeoutId_); + this.timeoutId_ = ol.global.setTimeout( + this.doZoom_.bind(this, map), timeLeft); + + mapBrowserEvent.preventDefault(); + stopEvent = true; + } + return !stopEvent; +}; + + +/** + * @private + * @param {ol.Map} map Map. + */ +ol.interaction.MouseWheelZoom.prototype.doZoom_ = function(map) { + var maxDelta = ol.MOUSEWHEELZOOM_MAXDELTA; + var delta = ol.math.clamp(this.delta_, -maxDelta, maxDelta); + + var view = map.getView(); + goog.asserts.assert(view, 'map must have view'); + + map.render(); + ol.interaction.Interaction.zoomByDelta(map, view, -delta, this.lastAnchor_, + this.duration_); + + this.delta_ = 0; + this.lastAnchor_ = null; + this.startTime_ = undefined; + this.timeoutId_ = undefined; +}; + + +/** + * Enable or disable using the mouse's location as an anchor when zooming + * @param {boolean} useAnchor true to zoom to the mouse's location, false + * to zoom to the center of the map + * @api + */ +ol.interaction.MouseWheelZoom.prototype.setMouseAnchor = function(useAnchor) { + this.useAnchor_ = useAnchor; + if (!useAnchor) { + this.lastAnchor_ = null; + } +}; + +goog.provide('ol.interaction.PinchRotate'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.functions'); +goog.require('ol.ViewHint'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.interaction.Pointer'); + + +/** + * @classdesc + * Allows the user to rotate the map by twisting with two fingers + * on a touch screen. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.PinchRotateOptions=} opt_options Options. + * @api stable + */ +ol.interaction.PinchRotate = function(opt_options) { + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.PinchRotate.handleDownEvent_, + handleDragEvent: ol.interaction.PinchRotate.handleDragEvent_, + handleUpEvent: ol.interaction.PinchRotate.handleUpEvent_ + }); + + var options = opt_options || {}; + + /** + * @private + * @type {ol.Coordinate} + */ + this.anchor_ = null; + + /** + * @private + * @type {number|undefined} + */ + this.lastAngle_ = undefined; + + /** + * @private + * @type {boolean} + */ + this.rotating_ = false; + + /** + * @private + * @type {number} + */ + this.rotationDelta_ = 0.0; + + /** + * @private + * @type {number} + */ + this.threshold_ = options.threshold !== undefined ? options.threshold : 0.3; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 250; + +}; +ol.inherits(ol.interaction.PinchRotate, ol.interaction.Pointer); + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.PinchRotate} + * @private + */ +ol.interaction.PinchRotate.handleDragEvent_ = function(mapBrowserEvent) { + goog.asserts.assert(this.targetPointers.length >= 2, + 'length of this.targetPointers should be greater than or equal to 2'); + var rotationDelta = 0.0; + + var touch0 = this.targetPointers[0]; + var touch1 = this.targetPointers[1]; + + // angle between touches + var angle = Math.atan2( + touch1.clientY - touch0.clientY, + touch1.clientX - touch0.clientX); + + if (this.lastAngle_ !== undefined) { + var delta = angle - this.lastAngle_; + this.rotationDelta_ += delta; + if (!this.rotating_ && + Math.abs(this.rotationDelta_) > this.threshold_) { + this.rotating_ = true; + } + rotationDelta = delta; + } + this.lastAngle_ = angle; + + var map = mapBrowserEvent.map; + + // rotate anchor point. + // FIXME: should be the intersection point between the lines: + // touch0,touch1 and previousTouch0,previousTouch1 + var viewportPosition = map.getViewport().getBoundingClientRect(); + var centroid = ol.interaction.Pointer.centroid(this.targetPointers); + centroid[0] -= viewportPosition.left; + centroid[1] -= viewportPosition.top; + this.anchor_ = map.getCoordinateFromPixel(centroid); + + // rotate + if (this.rotating_) { + var view = map.getView(); + var rotation = view.getRotation(); + map.render(); + ol.interaction.Interaction.rotateWithoutConstraints(map, view, + rotation + rotationDelta, this.anchor_); + } +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.PinchRotate} + * @private + */ +ol.interaction.PinchRotate.handleUpEvent_ = function(mapBrowserEvent) { + if (this.targetPointers.length < 2) { + var map = mapBrowserEvent.map; + var view = map.getView(); + view.setHint(ol.ViewHint.INTERACTING, -1); + if (this.rotating_) { + var rotation = view.getRotation(); + ol.interaction.Interaction.rotate( + map, view, rotation, this.anchor_, this.duration_); + } + return false; + } else { + return true; + } +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.PinchRotate} + * @private + */ +ol.interaction.PinchRotate.handleDownEvent_ = function(mapBrowserEvent) { + if (this.targetPointers.length >= 2) { + var map = mapBrowserEvent.map; + this.anchor_ = null; + this.lastAngle_ = undefined; + this.rotating_ = false; + this.rotationDelta_ = 0.0; + if (!this.handlingDownUpSequence) { + map.getView().setHint(ol.ViewHint.INTERACTING, 1); + } + map.render(); + return true; + } else { + return false; + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.PinchRotate.prototype.shouldStopEvent = ol.functions.FALSE; + +goog.provide('ol.interaction.PinchZoom'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.functions'); +goog.require('ol.ViewHint'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.interaction.Pointer'); + + +/** + * @classdesc + * Allows the user to zoom the map by pinching with two fingers + * on a touch screen. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.PinchZoomOptions=} opt_options Options. + * @api stable + */ +ol.interaction.PinchZoom = function(opt_options) { + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.PinchZoom.handleDownEvent_, + handleDragEvent: ol.interaction.PinchZoom.handleDragEvent_, + handleUpEvent: ol.interaction.PinchZoom.handleUpEvent_ + }); + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {ol.Coordinate} + */ + this.anchor_ = null; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 400; + + /** + * @private + * @type {number|undefined} + */ + this.lastDistance_ = undefined; + + /** + * @private + * @type {number} + */ + this.lastScaleDelta_ = 1; + +}; +ol.inherits(ol.interaction.PinchZoom, ol.interaction.Pointer); + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.PinchZoom} + * @private + */ +ol.interaction.PinchZoom.handleDragEvent_ = function(mapBrowserEvent) { + goog.asserts.assert(this.targetPointers.length >= 2, + 'length of this.targetPointers should be 2 or more'); + var scaleDelta = 1.0; + + var touch0 = this.targetPointers[0]; + var touch1 = this.targetPointers[1]; + var dx = touch0.clientX - touch1.clientX; + var dy = touch0.clientY - touch1.clientY; + + // distance between touches + var distance = Math.sqrt(dx * dx + dy * dy); + + if (this.lastDistance_ !== undefined) { + scaleDelta = this.lastDistance_ / distance; + } + this.lastDistance_ = distance; + if (scaleDelta != 1.0) { + this.lastScaleDelta_ = scaleDelta; + } + + var map = mapBrowserEvent.map; + var view = map.getView(); + var resolution = view.getResolution(); + + // scale anchor point. + var viewportPosition = map.getViewport().getBoundingClientRect(); + var centroid = ol.interaction.Pointer.centroid(this.targetPointers); + centroid[0] -= viewportPosition.left; + centroid[1] -= viewportPosition.top; + this.anchor_ = map.getCoordinateFromPixel(centroid); + + // scale, bypass the resolution constraint + map.render(); + ol.interaction.Interaction.zoomWithoutConstraints( + map, view, resolution * scaleDelta, this.anchor_); + +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.PinchZoom} + * @private + */ +ol.interaction.PinchZoom.handleUpEvent_ = function(mapBrowserEvent) { + if (this.targetPointers.length < 2) { + var map = mapBrowserEvent.map; + var view = map.getView(); + view.setHint(ol.ViewHint.INTERACTING, -1); + var resolution = view.getResolution(); + // Zoom to final resolution, with an animation, and provide a + // direction not to zoom out/in if user was pinching in/out. + // Direction is > 0 if pinching out, and < 0 if pinching in. + var direction = this.lastScaleDelta_ - 1; + ol.interaction.Interaction.zoom(map, view, resolution, + this.anchor_, this.duration_, direction); + return false; + } else { + return true; + } +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.PinchZoom} + * @private + */ +ol.interaction.PinchZoom.handleDownEvent_ = function(mapBrowserEvent) { + if (this.targetPointers.length >= 2) { + var map = mapBrowserEvent.map; + this.anchor_ = null; + this.lastDistance_ = undefined; + this.lastScaleDelta_ = 1; + if (!this.handlingDownUpSequence) { + map.getView().setHint(ol.ViewHint.INTERACTING, 1); + } + map.render(); + return true; + } else { + return false; + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.PinchZoom.prototype.shouldStopEvent = ol.functions.FALSE; + +goog.provide('ol.interaction'); + +goog.require('ol'); +goog.require('ol.Collection'); +goog.require('ol.Kinetic'); +goog.require('ol.interaction.DoubleClickZoom'); +goog.require('ol.interaction.DragPan'); +goog.require('ol.interaction.DragRotate'); +goog.require('ol.interaction.DragZoom'); +goog.require('ol.interaction.KeyboardPan'); +goog.require('ol.interaction.KeyboardZoom'); +goog.require('ol.interaction.MouseWheelZoom'); +goog.require('ol.interaction.PinchRotate'); +goog.require('ol.interaction.PinchZoom'); + + +/** + * Set of interactions included in maps by default. Specific interactions can be + * excluded by setting the appropriate option to false in the constructor + * options, but the order of the interactions is fixed. If you want to specify + * a different order for interactions, you will need to create your own + * {@link ol.interaction.Interaction} instances and insert them into a + * {@link ol.Collection} in the order you want before creating your + * {@link ol.Map} instance. The default set of interactions, in sequence, is: + * * {@link ol.interaction.DragRotate} + * * {@link ol.interaction.DoubleClickZoom} + * * {@link ol.interaction.DragPan} + * * {@link ol.interaction.PinchRotate} + * * {@link ol.interaction.PinchZoom} + * * {@link ol.interaction.KeyboardPan} + * * {@link ol.interaction.KeyboardZoom} + * * {@link ol.interaction.MouseWheelZoom} + * * {@link ol.interaction.DragZoom} + * + * @param {olx.interaction.DefaultsOptions=} opt_options Defaults options. + * @return {ol.Collection.<ol.interaction.Interaction>} A collection of + * interactions to be used with the ol.Map constructor's interactions option. + * @api stable + */ +ol.interaction.defaults = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + var interactions = new ol.Collection(); + + var kinetic = new ol.Kinetic(-0.005, 0.05, 100); + + var altShiftDragRotate = options.altShiftDragRotate !== undefined ? + options.altShiftDragRotate : true; + if (altShiftDragRotate) { + interactions.push(new ol.interaction.DragRotate()); + } + + var doubleClickZoom = options.doubleClickZoom !== undefined ? + options.doubleClickZoom : true; + if (doubleClickZoom) { + interactions.push(new ol.interaction.DoubleClickZoom({ + delta: options.zoomDelta, + duration: options.zoomDuration + })); + } + + var dragPan = options.dragPan !== undefined ? options.dragPan : true; + if (dragPan) { + interactions.push(new ol.interaction.DragPan({ + kinetic: kinetic + })); + } + + var pinchRotate = options.pinchRotate !== undefined ? options.pinchRotate : + true; + if (pinchRotate) { + interactions.push(new ol.interaction.PinchRotate()); + } + + var pinchZoom = options.pinchZoom !== undefined ? options.pinchZoom : true; + if (pinchZoom) { + interactions.push(new ol.interaction.PinchZoom({ + duration: options.zoomDuration + })); + } + + var keyboard = options.keyboard !== undefined ? options.keyboard : true; + if (keyboard) { + interactions.push(new ol.interaction.KeyboardPan()); + interactions.push(new ol.interaction.KeyboardZoom({ + delta: options.zoomDelta, + duration: options.zoomDuration + })); + } + + var mouseWheelZoom = options.mouseWheelZoom !== undefined ? + options.mouseWheelZoom : true; + if (mouseWheelZoom) { + interactions.push(new ol.interaction.MouseWheelZoom({ + duration: options.zoomDuration + })); + } + + var shiftDragZoom = options.shiftDragZoom !== undefined ? + options.shiftDragZoom : true; + if (shiftDragZoom) { + interactions.push(new ol.interaction.DragZoom({ + duration: options.zoomDuration + })); + } + + return interactions; + +}; + +goog.provide('ol.layer.Group'); + +goog.require('goog.asserts'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEvent'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Object'); +goog.require('ol.ObjectEventType'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.layer.Base'); +goog.require('ol.object'); +goog.require('ol.source.State'); + + +/** + * @enum {string} + */ +ol.layer.GroupProperty = { + LAYERS: 'layers' +}; + + +/** + * @classdesc + * A {@link ol.Collection} of layers that are handled together. + * + * A generic `change` event is triggered when the group/Collection changes. + * + * @constructor + * @extends {ol.layer.Base} + * @param {olx.layer.GroupOptions=} opt_options Layer options. + * @api stable + */ +ol.layer.Group = function(opt_options) { + + var options = opt_options || {}; + var baseOptions = /** @type {olx.layer.GroupOptions} */ + (ol.object.assign({}, options)); + delete baseOptions.layers; + + var layers = options.layers; + + ol.layer.Base.call(this, baseOptions); + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.layersListenerKeys_ = []; + + /** + * @private + * @type {Object.<string, Array.<ol.EventsKey>>} + */ + this.listenerKeys_ = {}; + + ol.events.listen(this, + ol.Object.getChangeEventType(ol.layer.GroupProperty.LAYERS), + this.handleLayersChanged_, this); + + if (layers) { + if (Array.isArray(layers)) { + layers = new ol.Collection(layers.slice()); + } else { + goog.asserts.assertInstanceof(layers, ol.Collection, + 'layers should be an ol.Collection'); + layers = layers; + } + } else { + layers = new ol.Collection(); + } + + this.setLayers(layers); + +}; +ol.inherits(ol.layer.Group, ol.layer.Base); + + +/** + * @private + */ +ol.layer.Group.prototype.handleLayerChange_ = function() { + if (this.getVisible()) { + this.changed(); + } +}; + + +/** + * @param {ol.events.Event} event Event. + * @private + */ +ol.layer.Group.prototype.handleLayersChanged_ = function(event) { + this.layersListenerKeys_.forEach(ol.events.unlistenByKey); + this.layersListenerKeys_.length = 0; + + var layers = this.getLayers(); + this.layersListenerKeys_.push( + ol.events.listen(layers, ol.CollectionEventType.ADD, + this.handleLayersAdd_, this), + ol.events.listen(layers, ol.CollectionEventType.REMOVE, + this.handleLayersRemove_, this)); + + for (var id in this.listenerKeys_) { + this.listenerKeys_[id].forEach(ol.events.unlistenByKey); + } + ol.object.clear(this.listenerKeys_); + + var layersArray = layers.getArray(); + var i, ii, layer; + for (i = 0, ii = layersArray.length; i < ii; i++) { + layer = layersArray[i]; + this.listenerKeys_[goog.getUid(layer).toString()] = [ + ol.events.listen(layer, ol.ObjectEventType.PROPERTYCHANGE, + this.handleLayerChange_, this), + ol.events.listen(layer, ol.events.EventType.CHANGE, + this.handleLayerChange_, this) + ]; + } + + this.changed(); +}; + + +/** + * @param {ol.CollectionEvent} collectionEvent Collection event. + * @private + */ +ol.layer.Group.prototype.handleLayersAdd_ = function(collectionEvent) { + var layer = /** @type {ol.layer.Base} */ (collectionEvent.element); + var key = goog.getUid(layer).toString(); + goog.asserts.assert(!(key in this.listenerKeys_), + 'listeners already registered'); + this.listenerKeys_[key] = [ + ol.events.listen(layer, ol.ObjectEventType.PROPERTYCHANGE, + this.handleLayerChange_, this), + ol.events.listen(layer, ol.events.EventType.CHANGE, + this.handleLayerChange_, this) + ]; + this.changed(); +}; + + +/** + * @param {ol.CollectionEvent} collectionEvent Collection event. + * @private + */ +ol.layer.Group.prototype.handleLayersRemove_ = function(collectionEvent) { + var layer = /** @type {ol.layer.Base} */ (collectionEvent.element); + var key = goog.getUid(layer).toString(); + goog.asserts.assert(key in this.listenerKeys_, 'no listeners to unregister'); + this.listenerKeys_[key].forEach(ol.events.unlistenByKey); + delete this.listenerKeys_[key]; + this.changed(); +}; + + +/** + * Returns the {@link ol.Collection collection} of {@link ol.layer.Layer layers} + * in this group. + * @return {!ol.Collection.<ol.layer.Base>} Collection of + * {@link ol.layer.Base layers} that are part of this group. + * @observable + * @api stable + */ +ol.layer.Group.prototype.getLayers = function() { + return /** @type {!ol.Collection.<ol.layer.Base>} */ (this.get( + ol.layer.GroupProperty.LAYERS)); +}; + + +/** + * Set the {@link ol.Collection collection} of {@link ol.layer.Layer layers} + * in this group. + * @param {!ol.Collection.<ol.layer.Base>} layers Collection of + * {@link ol.layer.Base layers} that are part of this group. + * @observable + * @api stable + */ +ol.layer.Group.prototype.setLayers = function(layers) { + this.set(ol.layer.GroupProperty.LAYERS, layers); +}; + + +/** + * @inheritDoc + */ +ol.layer.Group.prototype.getLayersArray = function(opt_array) { + var array = opt_array !== undefined ? opt_array : []; + this.getLayers().forEach(function(layer) { + layer.getLayersArray(array); + }); + return array; +}; + + +/** + * @inheritDoc + */ +ol.layer.Group.prototype.getLayerStatesArray = function(opt_states) { + var states = opt_states !== undefined ? opt_states : []; + + var pos = states.length; + + this.getLayers().forEach(function(layer) { + layer.getLayerStatesArray(states); + }); + + var ownLayerState = this.getLayerState(); + var i, ii, layerState; + for (i = pos, ii = states.length; i < ii; i++) { + layerState = states[i]; + layerState.opacity *= ownLayerState.opacity; + layerState.visible = layerState.visible && ownLayerState.visible; + layerState.maxResolution = Math.min( + layerState.maxResolution, ownLayerState.maxResolution); + layerState.minResolution = Math.max( + layerState.minResolution, ownLayerState.minResolution); + if (ownLayerState.extent !== undefined) { + if (layerState.extent !== undefined) { + layerState.extent = ol.extent.getIntersection( + layerState.extent, ownLayerState.extent); + } else { + layerState.extent = ownLayerState.extent; + } + } + } + + return states; +}; + + +/** + * @inheritDoc + */ +ol.layer.Group.prototype.getSourceState = function() { + return ol.source.State.READY; +}; + +goog.provide('ol.proj.EPSG3857'); + +goog.require('goog.asserts'); +goog.require('ol.math'); +goog.require('ol.proj'); +goog.require('ol.proj.Projection'); +goog.require('ol.proj.Units'); + + +/** + * @classdesc + * Projection object for web/spherical Mercator (EPSG:3857). + * + * @constructor + * @extends {ol.proj.Projection} + * @param {string} code Code. + * @private + */ +ol.proj.EPSG3857_ = function(code) { + ol.proj.Projection.call(this, { + code: code, + units: ol.proj.Units.METERS, + extent: ol.proj.EPSG3857.EXTENT, + global: true, + worldExtent: ol.proj.EPSG3857.WORLD_EXTENT + }); +}; +ol.inherits(ol.proj.EPSG3857_, ol.proj.Projection); + + +/** + * @inheritDoc + */ +ol.proj.EPSG3857_.prototype.getPointResolution = function(resolution, point) { + return resolution / ol.math.cosh(point[1] / ol.proj.EPSG3857.RADIUS); +}; + + +/** + * @const + * @type {number} + */ +ol.proj.EPSG3857.RADIUS = 6378137; + + +/** + * @const + * @type {number} + */ +ol.proj.EPSG3857.HALF_SIZE = Math.PI * ol.proj.EPSG3857.RADIUS; + + +/** + * @const + * @type {ol.Extent} + */ +ol.proj.EPSG3857.EXTENT = [ + -ol.proj.EPSG3857.HALF_SIZE, -ol.proj.EPSG3857.HALF_SIZE, + ol.proj.EPSG3857.HALF_SIZE, ol.proj.EPSG3857.HALF_SIZE +]; + + +/** + * @const + * @type {ol.Extent} + */ +ol.proj.EPSG3857.WORLD_EXTENT = [-180, -85, 180, 85]; + + +/** + * Lists several projection codes with the same meaning as EPSG:3857. + * + * @type {Array.<string>} + */ +ol.proj.EPSG3857.CODES = [ + 'EPSG:3857', + 'EPSG:102100', + 'EPSG:102113', + 'EPSG:900913', + 'urn:ogc:def:crs:EPSG:6.18:3:3857', + 'urn:ogc:def:crs:EPSG::3857', + 'http://www.opengis.net/gml/srs/epsg.xml#3857' +]; + + +/** + * Projections equal to EPSG:3857. + * + * @const + * @type {Array.<ol.proj.Projection>} + */ +ol.proj.EPSG3857.PROJECTIONS = ol.proj.EPSG3857.CODES.map(function(code) { + return new ol.proj.EPSG3857_(code); +}); + + +/** + * Transformation from EPSG:4326 to EPSG:3857. + * + * @param {Array.<number>} input Input array of coordinate values. + * @param {Array.<number>=} opt_output Output array of coordinate values. + * @param {number=} opt_dimension Dimension (default is `2`). + * @return {Array.<number>} Output array of coordinate values. + */ +ol.proj.EPSG3857.fromEPSG4326 = function(input, opt_output, opt_dimension) { + var length = input.length, + dimension = opt_dimension > 1 ? opt_dimension : 2, + output = opt_output; + if (output === undefined) { + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } + } + goog.asserts.assert(output.length % dimension === 0, + 'modulus of output.length with dimension should be 0'); + for (var i = 0; i < length; i += dimension) { + output[i] = ol.proj.EPSG3857.RADIUS * Math.PI * input[i] / 180; + output[i + 1] = ol.proj.EPSG3857.RADIUS * + Math.log(Math.tan(Math.PI * (input[i + 1] + 90) / 360)); + } + return output; +}; + + +/** + * Transformation from EPSG:3857 to EPSG:4326. + * + * @param {Array.<number>} input Input array of coordinate values. + * @param {Array.<number>=} opt_output Output array of coordinate values. + * @param {number=} opt_dimension Dimension (default is `2`). + * @return {Array.<number>} Output array of coordinate values. + */ +ol.proj.EPSG3857.toEPSG4326 = function(input, opt_output, opt_dimension) { + var length = input.length, + dimension = opt_dimension > 1 ? opt_dimension : 2, + output = opt_output; + if (output === undefined) { + if (dimension > 2) { + // preserve values beyond second dimension + output = input.slice(); + } else { + output = new Array(length); + } + } + goog.asserts.assert(output.length % dimension === 0, + 'modulus of output.length with dimension should be 0'); + for (var i = 0; i < length; i += dimension) { + output[i] = 180 * input[i] / (ol.proj.EPSG3857.RADIUS * Math.PI); + output[i + 1] = 360 * Math.atan( + Math.exp(input[i + 1] / ol.proj.EPSG3857.RADIUS)) / Math.PI - 90; + } + return output; +}; + +goog.provide('ol.sphere.WGS84'); + +goog.require('ol.Sphere'); + + +/** + * A sphere with radius equal to the semi-major axis of the WGS84 ellipsoid. + * @const + * @type {ol.Sphere} + */ +ol.sphere.WGS84 = new ol.Sphere(6378137); + +goog.provide('ol.proj.EPSG4326'); + +goog.require('ol.proj'); +goog.require('ol.proj.Projection'); +goog.require('ol.proj.Units'); +goog.require('ol.sphere.WGS84'); + + +/** + * @classdesc + * Projection object for WGS84 geographic coordinates (EPSG:4326). + * + * Note that OpenLayers does not strictly comply with the EPSG definition. + * The EPSG registry defines 4326 as a CRS for Latitude,Longitude (y,x). + * OpenLayers treats EPSG:4326 as a pseudo-projection, with x,y coordinates. + * + * @constructor + * @extends {ol.proj.Projection} + * @param {string} code Code. + * @param {string=} opt_axisOrientation Axis orientation. + * @private + */ +ol.proj.EPSG4326_ = function(code, opt_axisOrientation) { + ol.proj.Projection.call(this, { + code: code, + units: ol.proj.Units.DEGREES, + extent: ol.proj.EPSG4326.EXTENT, + axisOrientation: opt_axisOrientation, + global: true, + metersPerUnit: ol.proj.EPSG4326.METERS_PER_UNIT, + worldExtent: ol.proj.EPSG4326.EXTENT + }); +}; +ol.inherits(ol.proj.EPSG4326_, ol.proj.Projection); + + +/** + * @inheritDoc + */ +ol.proj.EPSG4326_.prototype.getPointResolution = function(resolution, point) { + return resolution; +}; + + +/** + * Extent of the EPSG:4326 projection which is the whole world. + * + * @const + * @type {ol.Extent} + */ +ol.proj.EPSG4326.EXTENT = [-180, -90, 180, 90]; + + +/** + * @const + * @type {number} + */ +ol.proj.EPSG4326.METERS_PER_UNIT = Math.PI * ol.sphere.WGS84.radius / 180; + + +/** + * Projections equal to EPSG:4326. + * + * @const + * @type {Array.<ol.proj.Projection>} + */ +ol.proj.EPSG4326.PROJECTIONS = [ + new ol.proj.EPSG4326_('CRS:84'), + new ol.proj.EPSG4326_('EPSG:4326', 'neu'), + new ol.proj.EPSG4326_('urn:ogc:def:crs:EPSG::4326', 'neu'), + new ol.proj.EPSG4326_('urn:ogc:def:crs:EPSG:6.6:4326', 'neu'), + new ol.proj.EPSG4326_('urn:ogc:def:crs:OGC:1.3:CRS84'), + new ol.proj.EPSG4326_('urn:ogc:def:crs:OGC:2:84'), + new ol.proj.EPSG4326_('http://www.opengis.net/gml/srs/epsg.xml#4326', 'neu'), + new ol.proj.EPSG4326_('urn:x-ogc:def:crs:EPSG:4326', 'neu') +]; + +goog.provide('ol.proj.common'); + +goog.require('ol.proj'); +goog.require('ol.proj.EPSG3857'); +goog.require('ol.proj.EPSG4326'); + + +/** + * FIXME empty description for jsdoc + * @api + */ +ol.proj.common.add = function() { + // Add transformations that don't alter coordinates to convert within set of + // projections with equal meaning. + ol.proj.addEquivalentProjections(ol.proj.EPSG3857.PROJECTIONS); + ol.proj.addEquivalentProjections(ol.proj.EPSG4326.PROJECTIONS); + // Add transformations to convert EPSG:4326 like coordinates to EPSG:3857 like + // coordinates and back. + ol.proj.addEquivalentTransforms( + ol.proj.EPSG4326.PROJECTIONS, + ol.proj.EPSG3857.PROJECTIONS, + ol.proj.EPSG3857.fromEPSG4326, + ol.proj.EPSG3857.toEPSG4326); +}; + +goog.provide('ol.layer.Image'); + +goog.require('ol.layer.Layer'); + + +/** + * @classdesc + * Server-rendered images that are available for arbitrary extents and + * resolutions. + * Note that any property set in the options is set as a {@link ol.Object} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {ol.layer.Layer} + * @fires ol.render.Event + * @param {olx.layer.ImageOptions=} opt_options Layer options. + * @api stable + */ +ol.layer.Image = function(opt_options) { + var options = opt_options ? opt_options : {}; + ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (options)); +}; +ol.inherits(ol.layer.Image, ol.layer.Layer); + + +/** + * Return the associated {@link ol.source.Image source} of the image layer. + * @function + * @return {ol.source.Image} Source. + * @api stable + */ +ol.layer.Image.prototype.getSource; + +goog.provide('ol.layer.Tile'); + +goog.require('ol'); +goog.require('ol.layer.Layer'); +goog.require('ol.object'); + + +/** + * @enum {string} + */ +ol.layer.TileProperty = { + PRELOAD: 'preload', + USE_INTERIM_TILES_ON_ERROR: 'useInterimTilesOnError' +}; + + +/** + * @classdesc + * For layer sources that provide pre-rendered, tiled images in grids that are + * organized by zoom levels for specific resolutions. + * Note that any property set in the options is set as a {@link ol.Object} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {ol.layer.Layer} + * @fires ol.render.Event + * @param {olx.layer.TileOptions=} opt_options Tile layer options. + * @api stable + */ +ol.layer.Tile = function(opt_options) { + var options = opt_options ? opt_options : {}; + + var baseOptions = ol.object.assign({}, options); + + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (baseOptions)); + + this.setPreload(options.preload !== undefined ? options.preload : 0); + this.setUseInterimTilesOnError(options.useInterimTilesOnError !== undefined ? + options.useInterimTilesOnError : true); +}; +ol.inherits(ol.layer.Tile, ol.layer.Layer); + + +/** + * Return the level as number to which we will preload tiles up to. + * @return {number} The level to preload tiles up to. + * @observable + * @api + */ +ol.layer.Tile.prototype.getPreload = function() { + return /** @type {number} */ (this.get(ol.layer.TileProperty.PRELOAD)); +}; + + +/** + * Return the associated {@link ol.source.Tile tilesource} of the layer. + * @function + * @return {ol.source.Tile} Source. + * @api stable + */ +ol.layer.Tile.prototype.getSource; + + +/** + * Set the level as number to which we will preload tiles up to. + * @param {number} preload The level to preload tiles up to. + * @observable + * @api + */ +ol.layer.Tile.prototype.setPreload = function(preload) { + this.set(ol.layer.TileProperty.PRELOAD, preload); +}; + + +/** + * Whether we use interim tiles on error. + * @return {boolean} Use interim tiles on error. + * @observable + * @api + */ +ol.layer.Tile.prototype.getUseInterimTilesOnError = function() { + return /** @type {boolean} */ ( + this.get(ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR)); +}; + + +/** + * Set whether we use interim tiles on error. + * @param {boolean} useInterimTilesOnError Use interim tiles on error. + * @observable + * @api + */ +ol.layer.Tile.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) { + this.set( + ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); +}; + +goog.provide('ol.render.canvas'); + + +/** + * @const + * @type {string} + */ +ol.render.canvas.defaultFont = '10px sans-serif'; + + +/** + * @const + * @type {ol.Color} + */ +ol.render.canvas.defaultFillStyle = [0, 0, 0, 1]; + + +/** + * @const + * @type {string} + */ +ol.render.canvas.defaultLineCap = 'round'; + + +/** + * @const + * @type {Array.<number>} + */ +ol.render.canvas.defaultLineDash = []; + + +/** + * @const + * @type {string} + */ +ol.render.canvas.defaultLineJoin = 'round'; + + +/** + * @const + * @type {number} + */ +ol.render.canvas.defaultMiterLimit = 10; + + +/** + * @const + * @type {ol.Color} + */ +ol.render.canvas.defaultStrokeStyle = [0, 0, 0, 1]; + + +/** + * @const + * @type {string} + */ +ol.render.canvas.defaultTextAlign = 'center'; + + +/** + * @const + * @type {string} + */ +ol.render.canvas.defaultTextBaseline = 'middle'; + + +/** + * @const + * @type {number} + */ +ol.render.canvas.defaultLineWidth = 1; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {number} rotation Rotation. + * @param {number} offsetX X offset. + * @param {number} offsetY Y offset. + */ +ol.render.canvas.rotateAtOffset = function(context, rotation, offsetX, offsetY) { + if (rotation !== 0) { + context.translate(offsetX, offsetY); + context.rotate(rotation); + context.translate(-offsetX, -offsetY); + } +}; + +goog.provide('ol.style.Fill'); + +goog.require('ol.color'); + + +/** + * @classdesc + * Set fill style for vector features. + * + * @constructor + * @param {olx.style.FillOptions=} opt_options Options. + * @api + */ +ol.style.Fill = function(opt_options) { + + var options = opt_options || {}; + + /** + * @private + * @type {ol.Color|ol.ColorLike} + */ + this.color_ = options.color !== undefined ? options.color : null; + + /** + * @private + * @type {string|undefined} + */ + this.checksum_ = undefined; +}; + + +/** + * Get the fill color. + * @return {ol.Color|ol.ColorLike} Color. + * @api + */ +ol.style.Fill.prototype.getColor = function() { + return this.color_; +}; + + +/** + * Set the color. + * + * @param {ol.Color|ol.ColorLike} color Color. + * @api + */ +ol.style.Fill.prototype.setColor = function(color) { + this.color_ = color; + this.checksum_ = undefined; +}; + + +/** + * @return {string} The checksum. + */ +ol.style.Fill.prototype.getChecksum = function() { + if (this.checksum_ === undefined) { + if ( + this.color_ instanceof CanvasPattern || + this.color_ instanceof CanvasGradient + ) { + this.checksum_ = goog.getUid(this.color_).toString(); + } else { + this.checksum_ = 'f' + (this.color_ ? + ol.color.asString(this.color_) : '-'); + } + } + + return this.checksum_; +}; + +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Namespace with crypto related helper functions. + */ + +goog.provide('goog.crypt'); + +goog.require('goog.array'); +goog.require('goog.asserts'); + + +/** + * Turns a string into an array of bytes; a "byte" being a JS number in the + * range 0-255. + * @param {string} str String value to arrify. + * @return {!Array<number>} Array of numbers corresponding to the + * UCS character codes of each character in str. + */ +goog.crypt.stringToByteArray = function(str) { + var output = [], p = 0; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + while (c > 0xff) { + output[p++] = c & 0xff; + c >>= 8; + } + output[p++] = c; + } + return output; +}; + + +/** + * Turns an array of numbers into the string given by the concatenation of the + * characters to which the numbers correspond. + * @param {!Uint8Array|!Array<number>} bytes Array of numbers representing + * characters. + * @return {string} Stringification of the array. + */ +goog.crypt.byteArrayToString = function(bytes) { + var CHUNK_SIZE = 8192; + + // Special-case the simple case for speed's sake. + if (bytes.length <= CHUNK_SIZE) { + return String.fromCharCode.apply(null, bytes); + } + + // The remaining logic splits conversion by chunks since + // Function#apply() has a maximum parameter count. + // See discussion: http://goo.gl/LrWmZ9 + + var str = ''; + for (var i = 0; i < bytes.length; i += CHUNK_SIZE) { + var chunk = goog.array.slice(bytes, i, i + CHUNK_SIZE); + str += String.fromCharCode.apply(null, chunk); + } + return str; +}; + + +/** + * Turns an array of numbers into the hex string given by the concatenation of + * the hex values to which the numbers correspond. + * @param {Uint8Array|Array<number>} array Array of numbers representing + * characters. + * @return {string} Hex string. + */ +goog.crypt.byteArrayToHex = function(array) { + return goog.array + .map( + array, + function(numByte) { + var hexByte = numByte.toString(16); + return hexByte.length > 1 ? hexByte : '0' + hexByte; + }) + .join(''); +}; + + +/** + * Converts a hex string into an integer array. + * @param {string} hexString Hex string of 16-bit integers (two characters + * per integer). + * @return {!Array<number>} Array of {0,255} integers for the given string. + */ +goog.crypt.hexToByteArray = function(hexString) { + goog.asserts.assert( + hexString.length % 2 == 0, 'Key string length must be multiple of 2'); + var arr = []; + for (var i = 0; i < hexString.length; i += 2) { + arr.push(parseInt(hexString.substring(i, i + 2), 16)); + } + return arr; +}; + + +/** + * Converts a JS string to a UTF-8 "byte" array. + * @param {string} str 16-bit unicode string. + * @return {!Array<number>} UTF-8 byte array. + */ +goog.crypt.stringToUtf8ByteArray = function(str) { + // TODO(user): Use native implementations if/when available + var out = [], p = 0; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c < 128) { + out[p++] = c; + } else if (c < 2048) { + out[p++] = (c >> 6) | 192; + out[p++] = (c & 63) | 128; + } else if ( + ((c & 0xFC00) == 0xD800) && (i + 1) < str.length && + ((str.charCodeAt(i + 1) & 0xFC00) == 0xDC00)) { + // Surrogate Pair + c = 0x10000 + ((c & 0x03FF) << 10) + (str.charCodeAt(++i) & 0x03FF); + out[p++] = (c >> 18) | 240; + out[p++] = ((c >> 12) & 63) | 128; + out[p++] = ((c >> 6) & 63) | 128; + out[p++] = (c & 63) | 128; + } else { + out[p++] = (c >> 12) | 224; + out[p++] = ((c >> 6) & 63) | 128; + out[p++] = (c & 63) | 128; + } + } + return out; +}; + + +/** + * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. + * @param {Uint8Array|Array<number>} bytes UTF-8 byte array. + * @return {string} 16-bit Unicode string. + */ +goog.crypt.utf8ByteArrayToString = function(bytes) { + // TODO(user): Use native implementations if/when available + var out = [], pos = 0, c = 0; + while (pos < bytes.length) { + var c1 = bytes[pos++]; + if (c1 < 128) { + out[c++] = String.fromCharCode(c1); + } else if (c1 > 191 && c1 < 224) { + var c2 = bytes[pos++]; + out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63); + } else if (c1 > 239 && c1 < 365) { + // Surrogate Pair + var c2 = bytes[pos++]; + var c3 = bytes[pos++]; + var c4 = bytes[pos++]; + var u = ((c1 & 7) << 18 | (c2 & 63) << 12 | (c3 & 63) << 6 | c4 & 63) - + 0x10000; + out[c++] = String.fromCharCode(0xD800 + (u >> 10)); + out[c++] = String.fromCharCode(0xDC00 + (u & 1023)); + } else { + var c2 = bytes[pos++]; + var c3 = bytes[pos++]; + out[c++] = + String.fromCharCode((c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63); + } + } + return out.join(''); +}; + + +/** + * XOR two byte arrays. + * @param {!Uint8Array|!Int8Array|!Array<number>} bytes1 Byte array 1. + * @param {!Uint8Array|!Int8Array|!Array<number>} bytes2 Byte array 2. + * @return {!Array<number>} Resulting XOR of the two byte arrays. + */ +goog.crypt.xorByteArray = function(bytes1, bytes2) { + goog.asserts.assert( + bytes1.length == bytes2.length, 'XOR array lengths must match'); + + var result = []; + for (var i = 0; i < bytes1.length; i++) { + result.push(bytes1[i] ^ bytes2[i]); + } + return result; +}; + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Abstract cryptographic hash interface. + * + * See goog.crypt.Sha1 and goog.crypt.Md5 for sample implementations. + * + */ + +goog.provide('goog.crypt.Hash'); + + + +/** + * Create a cryptographic hash instance. + * + * @constructor + * @struct + */ +goog.crypt.Hash = function() { + /** + * The block size for the hasher. + * @type {number} + */ + this.blockSize = -1; +}; + + +/** + * Resets the internal accumulator. + */ +goog.crypt.Hash.prototype.reset = goog.abstractMethod; + + +/** + * Adds a byte array (array with values in [0-255] range) or a string (might + * only contain 8-bit, i.e., Latin1 characters) to the internal accumulator. + * + * Many hash functions operate on blocks of data and implement optimizations + * when a full chunk of data is readily available. Hence it is often preferable + * to provide large chunks of data (a kilobyte or more) than to repeatedly + * call the update method with few tens of bytes. If this is not possible, or + * not feasible, it might be good to provide data in multiplies of hash block + * size (often 64 bytes). Please see the implementation and performance tests + * of your favourite hash. + * + * @param {Array<number>|Uint8Array|string} bytes Data used for the update. + * @param {number=} opt_length Number of bytes to use. + */ +goog.crypt.Hash.prototype.update = goog.abstractMethod; + + +/** + * @return {!Array<number>} The finalized hash computed + * from the internal accumulator. + */ +goog.crypt.Hash.prototype.digest = goog.abstractMethod; + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview MD5 cryptographic hash. + * Implementation of http://tools.ietf.org/html/rfc1321 with common + * optimizations and tweaks (see http://en.wikipedia.org/wiki/MD5). + * + * Usage: + * var md5 = new goog.crypt.Md5(); + * md5.update(bytes); + * var hash = md5.digest(); + * + * Performance: + * Chrome 23 ~680 Mbit/s + * Chrome 13 (in a VM) ~250 Mbit/s + * Firefox 6.0 (in a VM) ~100 Mbit/s + * IE9 (in a VM) ~27 Mbit/s + * Firefox 3.6 ~15 Mbit/s + * IE8 (in a VM) ~13 Mbit/s + * + */ + +goog.provide('goog.crypt.Md5'); + +goog.require('goog.crypt.Hash'); + + + +/** + * MD5 cryptographic hash constructor. + * @constructor + * @extends {goog.crypt.Hash} + * @final + * @struct + */ +goog.crypt.Md5 = function() { + goog.crypt.Md5.base(this, 'constructor'); + + this.blockSize = 512 / 8; + + /** + * Holds the current values of accumulated A-D variables (MD buffer). + * @type {!Array<number>} + * @private + */ + this.chain_ = new Array(4); + + /** + * A buffer holding the data until the whole block can be processed. + * @type {!Array<number>} + * @private + */ + this.block_ = new Array(this.blockSize); + + /** + * The length of yet-unprocessed data as collected in the block. + * @type {number} + * @private + */ + this.blockLength_ = 0; + + /** + * The total length of the message so far. + * @type {number} + * @private + */ + this.totalLength_ = 0; + + this.reset(); +}; +goog.inherits(goog.crypt.Md5, goog.crypt.Hash); + + +/** + * Integer rotation constants used by the abbreviated implementation. + * They are hardcoded in the unrolled implementation, so it is left + * here commented out. + * @type {Array<number>} + * @private + * +goog.crypt.Md5.S_ = [ + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21 +]; + */ + +/** + * Sine function constants used by the abbreviated implementation. + * They are hardcoded in the unrolled implementation, so it is left + * here commented out. + * @type {Array<number>} + * @private + * +goog.crypt.Md5.T_ = [ + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391 +]; + */ + + +/** @override */ +goog.crypt.Md5.prototype.reset = function() { + this.chain_[0] = 0x67452301; + this.chain_[1] = 0xefcdab89; + this.chain_[2] = 0x98badcfe; + this.chain_[3] = 0x10325476; + + this.blockLength_ = 0; + this.totalLength_ = 0; +}; + + +/** + * Internal compress helper function. It takes a block of data (64 bytes) + * and updates the accumulator. + * @param {Array<number>|Uint8Array|string} buf The block to compress. + * @param {number=} opt_offset Offset of the block in the buffer. + * @private + */ +goog.crypt.Md5.prototype.compress_ = function(buf, opt_offset) { + if (!opt_offset) { + opt_offset = 0; + } + + // We allocate the array every time, but it's cheap in practice. + var X = new Array(16); + + // Get 16 little endian words. It is not worth unrolling this for Chrome 11. + if (goog.isString(buf)) { + for (var i = 0; i < 16; ++i) { + X[i] = (buf.charCodeAt(opt_offset++)) | + (buf.charCodeAt(opt_offset++) << 8) | + (buf.charCodeAt(opt_offset++) << 16) | + (buf.charCodeAt(opt_offset++) << 24); + } + } else { + for (var i = 0; i < 16; ++i) { + X[i] = (buf[opt_offset++]) | (buf[opt_offset++] << 8) | + (buf[opt_offset++] << 16) | (buf[opt_offset++] << 24); + } + } + + var A = this.chain_[0]; + var B = this.chain_[1]; + var C = this.chain_[2]; + var D = this.chain_[3]; + var sum = 0; + + /* + * This is an abbreviated implementation, it is left here commented out for + * reference purposes. See below for an unrolled version in use. + * + var f, n, tmp; + for (var i = 0; i < 64; ++i) { + + if (i < 16) { + f = (D ^ (B & (C ^ D))); + n = i; + } else if (i < 32) { + f = (C ^ (D & (B ^ C))); + n = (5 * i + 1) % 16; + } else if (i < 48) { + f = (B ^ C ^ D); + n = (3 * i + 5) % 16; + } else { + f = (C ^ (B | (~D))); + n = (7 * i) % 16; + } + + tmp = D; + D = C; + C = B; + sum = (A + f + goog.crypt.Md5.T_[i] + X[n]) & 0xffffffff; + B += ((sum << goog.crypt.Md5.S_[i]) & 0xffffffff) | + (sum >>> (32 - goog.crypt.Md5.S_[i])); + A = tmp; + } + */ + + /* + * This is an unrolled MD5 implementation, which gives ~30% speedup compared + * to the abbreviated implementation above, as measured on Chrome 11. It is + * important to keep 32-bit croppings to minimum and inline the integer + * rotation. + */ + sum = (A + (D ^ (B & (C ^ D))) + X[0] + 0xd76aa478) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[1] + 0xe8c7b756) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[2] + 0x242070db) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[3] + 0xc1bdceee) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (D ^ (B & (C ^ D))) + X[4] + 0xf57c0faf) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[5] + 0x4787c62a) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[6] + 0xa8304613) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[7] + 0xfd469501) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (D ^ (B & (C ^ D))) + X[8] + 0x698098d8) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[9] + 0x8b44f7af) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[10] + 0xffff5bb1) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[11] + 0x895cd7be) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (D ^ (B & (C ^ D))) + X[12] + 0x6b901122) & 0xffffffff; + A = B + (((sum << 7) & 0xffffffff) | (sum >>> 25)); + sum = (D + (C ^ (A & (B ^ C))) + X[13] + 0xfd987193) & 0xffffffff; + D = A + (((sum << 12) & 0xffffffff) | (sum >>> 20)); + sum = (C + (B ^ (D & (A ^ B))) + X[14] + 0xa679438e) & 0xffffffff; + C = D + (((sum << 17) & 0xffffffff) | (sum >>> 15)); + sum = (B + (A ^ (C & (D ^ A))) + X[15] + 0x49b40821) & 0xffffffff; + B = C + (((sum << 22) & 0xffffffff) | (sum >>> 10)); + sum = (A + (C ^ (D & (B ^ C))) + X[1] + 0xf61e2562) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[6] + 0xc040b340) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[11] + 0x265e5a51) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[0] + 0xe9b6c7aa) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (C ^ (D & (B ^ C))) + X[5] + 0xd62f105d) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[10] + 0x02441453) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[15] + 0xd8a1e681) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[4] + 0xe7d3fbc8) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (C ^ (D & (B ^ C))) + X[9] + 0x21e1cde6) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[14] + 0xc33707d6) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[3] + 0xf4d50d87) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[8] + 0x455a14ed) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (C ^ (D & (B ^ C))) + X[13] + 0xa9e3e905) & 0xffffffff; + A = B + (((sum << 5) & 0xffffffff) | (sum >>> 27)); + sum = (D + (B ^ (C & (A ^ B))) + X[2] + 0xfcefa3f8) & 0xffffffff; + D = A + (((sum << 9) & 0xffffffff) | (sum >>> 23)); + sum = (C + (A ^ (B & (D ^ A))) + X[7] + 0x676f02d9) & 0xffffffff; + C = D + (((sum << 14) & 0xffffffff) | (sum >>> 18)); + sum = (B + (D ^ (A & (C ^ D))) + X[12] + 0x8d2a4c8a) & 0xffffffff; + B = C + (((sum << 20) & 0xffffffff) | (sum >>> 12)); + sum = (A + (B ^ C ^ D) + X[5] + 0xfffa3942) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[8] + 0x8771f681) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[11] + 0x6d9d6122) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[14] + 0xfde5380c) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (B ^ C ^ D) + X[1] + 0xa4beea44) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[4] + 0x4bdecfa9) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[7] + 0xf6bb4b60) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[10] + 0xbebfbc70) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (B ^ C ^ D) + X[13] + 0x289b7ec6) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[0] + 0xeaa127fa) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[3] + 0xd4ef3085) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[6] + 0x04881d05) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (B ^ C ^ D) + X[9] + 0xd9d4d039) & 0xffffffff; + A = B + (((sum << 4) & 0xffffffff) | (sum >>> 28)); + sum = (D + (A ^ B ^ C) + X[12] + 0xe6db99e5) & 0xffffffff; + D = A + (((sum << 11) & 0xffffffff) | (sum >>> 21)); + sum = (C + (D ^ A ^ B) + X[15] + 0x1fa27cf8) & 0xffffffff; + C = D + (((sum << 16) & 0xffffffff) | (sum >>> 16)); + sum = (B + (C ^ D ^ A) + X[2] + 0xc4ac5665) & 0xffffffff; + B = C + (((sum << 23) & 0xffffffff) | (sum >>> 9)); + sum = (A + (C ^ (B | (~D))) + X[0] + 0xf4292244) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[7] + 0x432aff97) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[14] + 0xab9423a7) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[5] + 0xfc93a039) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + sum = (A + (C ^ (B | (~D))) + X[12] + 0x655b59c3) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[3] + 0x8f0ccc92) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[10] + 0xffeff47d) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[1] + 0x85845dd1) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + sum = (A + (C ^ (B | (~D))) + X[8] + 0x6fa87e4f) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[15] + 0xfe2ce6e0) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[6] + 0xa3014314) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[13] + 0x4e0811a1) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + sum = (A + (C ^ (B | (~D))) + X[4] + 0xf7537e82) & 0xffffffff; + A = B + (((sum << 6) & 0xffffffff) | (sum >>> 26)); + sum = (D + (B ^ (A | (~C))) + X[11] + 0xbd3af235) & 0xffffffff; + D = A + (((sum << 10) & 0xffffffff) | (sum >>> 22)); + sum = (C + (A ^ (D | (~B))) + X[2] + 0x2ad7d2bb) & 0xffffffff; + C = D + (((sum << 15) & 0xffffffff) | (sum >>> 17)); + sum = (B + (D ^ (C | (~A))) + X[9] + 0xeb86d391) & 0xffffffff; + B = C + (((sum << 21) & 0xffffffff) | (sum >>> 11)); + + this.chain_[0] = (this.chain_[0] + A) & 0xffffffff; + this.chain_[1] = (this.chain_[1] + B) & 0xffffffff; + this.chain_[2] = (this.chain_[2] + C) & 0xffffffff; + this.chain_[3] = (this.chain_[3] + D) & 0xffffffff; +}; + + +/** @override */ +goog.crypt.Md5.prototype.update = function(bytes, opt_length) { + if (!goog.isDef(opt_length)) { + opt_length = bytes.length; + } + var lengthMinusBlock = opt_length - this.blockSize; + + // Copy some object properties to local variables in order to save on access + // time from inside the loop (~10% speedup was observed on Chrome 11). + var block = this.block_; + var blockLength = this.blockLength_; + var i = 0; + + // The outer while loop should execute at most twice. + while (i < opt_length) { + // When we have no data in the block to top up, we can directly process the + // input buffer (assuming it contains sufficient data). This gives ~30% + // speedup on Chrome 14 and ~70% speedup on Firefox 6.0, but requires that + // the data is provided in large chunks (or in multiples of 64 bytes). + if (blockLength == 0) { + while (i <= lengthMinusBlock) { + this.compress_(bytes, i); + i += this.blockSize; + } + } + + if (goog.isString(bytes)) { + while (i < opt_length) { + block[blockLength++] = bytes.charCodeAt(i++); + if (blockLength == this.blockSize) { + this.compress_(block); + blockLength = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } else { + while (i < opt_length) { + block[blockLength++] = bytes[i++]; + if (blockLength == this.blockSize) { + this.compress_(block); + blockLength = 0; + // Jump to the outer loop so we use the full-block optimization. + break; + } + } + } + } + + this.blockLength_ = blockLength; + this.totalLength_ += opt_length; +}; + + +/** @override */ +goog.crypt.Md5.prototype.digest = function() { + // This must accommodate at least 1 padding byte (0x80), 8 bytes of + // total bitlength, and must end at a 64-byte boundary. + var pad = new Array( + (this.blockLength_ < 56 ? this.blockSize : this.blockSize * 2) - + this.blockLength_); + + // Add padding: 0x80 0x00* + pad[0] = 0x80; + for (var i = 1; i < pad.length - 8; ++i) { + pad[i] = 0; + } + // Add the total number of bits, little endian 64-bit integer. + var totalBits = this.totalLength_ * 8; + for (var i = pad.length - 8; i < pad.length; ++i) { + pad[i] = totalBits & 0xff; + totalBits /= 0x100; // Don't use bit-shifting here! + } + this.update(pad); + + var digest = new Array(16); + var n = 0; + for (var i = 0; i < 4; ++i) { + for (var j = 0; j < 32; j += 8) { + digest[n++] = (this.chain_[i] >>> j) & 0xff; + } + } + return digest; +}; + +goog.provide('ol.style.Stroke'); + +goog.require('goog.crypt'); +goog.require('goog.crypt.Md5'); +goog.require('ol.color'); + + +/** + * @classdesc + * Set stroke style for vector features. + * Note that the defaults given are the Canvas defaults, which will be used if + * option is not defined. The `get` functions return whatever was entered in + * the options; they will not return the default. + * + * @constructor + * @param {olx.style.StrokeOptions=} opt_options Options. + * @api + */ +ol.style.Stroke = function(opt_options) { + + var options = opt_options || {}; + + /** + * @private + * @type {ol.Color|string} + */ + this.color_ = options.color !== undefined ? options.color : null; + + /** + * @private + * @type {string|undefined} + */ + this.lineCap_ = options.lineCap; + + /** + * @private + * @type {Array.<number>} + */ + this.lineDash_ = options.lineDash !== undefined ? options.lineDash : null; + + /** + * @private + * @type {string|undefined} + */ + this.lineJoin_ = options.lineJoin; + + /** + * @private + * @type {number|undefined} + */ + this.miterLimit_ = options.miterLimit; + + /** + * @private + * @type {number|undefined} + */ + this.width_ = options.width; + + /** + * @private + * @type {string|undefined} + */ + this.checksum_ = undefined; +}; + + +/** + * Get the stroke color. + * @return {ol.Color|string} Color. + * @api + */ +ol.style.Stroke.prototype.getColor = function() { + return this.color_; +}; + + +/** + * Get the line cap type for the stroke. + * @return {string|undefined} Line cap. + * @api + */ +ol.style.Stroke.prototype.getLineCap = function() { + return this.lineCap_; +}; + + +/** + * Get the line dash style for the stroke. + * @return {Array.<number>} Line dash. + * @api + */ +ol.style.Stroke.prototype.getLineDash = function() { + return this.lineDash_; +}; + + +/** + * Get the line join type for the stroke. + * @return {string|undefined} Line join. + * @api + */ +ol.style.Stroke.prototype.getLineJoin = function() { + return this.lineJoin_; +}; + + +/** + * Get the miter limit for the stroke. + * @return {number|undefined} Miter limit. + * @api + */ +ol.style.Stroke.prototype.getMiterLimit = function() { + return this.miterLimit_; +}; + + +/** + * Get the stroke width. + * @return {number|undefined} Width. + * @api + */ +ol.style.Stroke.prototype.getWidth = function() { + return this.width_; +}; + + +/** + * Set the color. + * + * @param {ol.Color|string} color Color. + * @api + */ +ol.style.Stroke.prototype.setColor = function(color) { + this.color_ = color; + this.checksum_ = undefined; +}; + + +/** + * Set the line cap. + * + * @param {string|undefined} lineCap Line cap. + * @api + */ +ol.style.Stroke.prototype.setLineCap = function(lineCap) { + this.lineCap_ = lineCap; + this.checksum_ = undefined; +}; + + +/** + * Set the line dash. + * + * Please note that Internet Explorer 10 and lower [do not support][mdn] the + * `setLineDash` method on the `CanvasRenderingContext2D` and therefore this + * property will have no visual effect in these browsers. + * + * [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash#Browser_compatibility + * + * @param {Array.<number>} lineDash Line dash. + * @api + */ +ol.style.Stroke.prototype.setLineDash = function(lineDash) { + this.lineDash_ = lineDash; + this.checksum_ = undefined; +}; + + +/** + * Set the line join. + * + * @param {string|undefined} lineJoin Line join. + * @api + */ +ol.style.Stroke.prototype.setLineJoin = function(lineJoin) { + this.lineJoin_ = lineJoin; + this.checksum_ = undefined; +}; + + +/** + * Set the miter limit. + * + * @param {number|undefined} miterLimit Miter limit. + * @api + */ +ol.style.Stroke.prototype.setMiterLimit = function(miterLimit) { + this.miterLimit_ = miterLimit; + this.checksum_ = undefined; +}; + + +/** + * Set the width. + * + * @param {number|undefined} width Width. + * @api + */ +ol.style.Stroke.prototype.setWidth = function(width) { + this.width_ = width; + this.checksum_ = undefined; +}; + + +/** + * @return {string} The checksum. + */ +ol.style.Stroke.prototype.getChecksum = function() { + if (this.checksum_ === undefined) { + var raw = 's' + + (this.color_ ? + ol.color.asString(this.color_) : '-') + ',' + + (this.lineCap_ !== undefined ? + this.lineCap_.toString() : '-') + ',' + + (this.lineDash_ ? + this.lineDash_.toString() : '-') + ',' + + (this.lineJoin_ !== undefined ? + this.lineJoin_ : '-') + ',' + + (this.miterLimit_ !== undefined ? + this.miterLimit_.toString() : '-') + ',' + + (this.width_ !== undefined ? + this.width_.toString() : '-'); + + var md5 = new goog.crypt.Md5(); + md5.update(raw); + this.checksum_ = goog.crypt.byteArrayToString(md5.digest()); + } + + return this.checksum_; +}; + +goog.provide('ol.style.Circle'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.color'); +goog.require('ol.colorlike'); +goog.require('ol.dom'); +goog.require('ol.has'); +goog.require('ol.render.canvas'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Image'); +goog.require('ol.style.ImageState'); +goog.require('ol.style.Stroke'); + + +/** + * @classdesc + * Set circle style for vector features. + * + * @constructor + * @param {olx.style.CircleOptions=} opt_options Options. + * @extends {ol.style.Image} + * @api + */ +ol.style.Circle = function(opt_options) { + + var options = opt_options || {}; + + /** + * @private + * @type {Array.<string>} + */ + this.checksums_ = null; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.hitDetectionCanvas_ = null; + + /** + * @private + * @type {ol.style.Fill} + */ + this.fill_ = options.fill !== undefined ? options.fill : null; + + /** + * @private + * @type {ol.style.Stroke} + */ + this.stroke_ = options.stroke !== undefined ? options.stroke : null; + + /** + * @private + * @type {number} + */ + this.radius_ = options.radius; + + /** + * @private + * @type {Array.<number>} + */ + this.origin_ = [0, 0]; + + /** + * @private + * @type {Array.<number>} + */ + this.anchor_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.imageSize_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.hitDetectionImageSize_ = null; + + this.render_(options.atlasManager); + + /** + * @type {boolean} + */ + var snapToPixel = options.snapToPixel !== undefined ? + options.snapToPixel : true; + + ol.style.Image.call(this, { + opacity: 1, + rotateWithView: false, + rotation: 0, + scale: 1, + snapToPixel: snapToPixel + }); + +}; +ol.inherits(ol.style.Circle, ol.style.Image); + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getAnchor = function() { + return this.anchor_; +}; + + +/** + * Get the fill style for the circle. + * @return {ol.style.Fill} Fill style. + * @api + */ +ol.style.Circle.prototype.getFill = function() { + return this.fill_; +}; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getHitDetectionImage = function(pixelRatio) { + return this.hitDetectionCanvas_; +}; + + +/** + * Get the image used to render the circle. + * @param {number} pixelRatio Pixel ratio. + * @return {HTMLCanvasElement} Canvas element. + * @api + */ +ol.style.Circle.prototype.getImage = function(pixelRatio) { + return this.canvas_; +}; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getImageState = function() { + return ol.style.ImageState.LOADED; +}; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getImageSize = function() { + return this.imageSize_; +}; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getHitDetectionImageSize = function() { + return this.hitDetectionImageSize_; +}; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getOrigin = function() { + return this.origin_; +}; + + +/** + * Get the circle radius. + * @return {number} Radius. + * @api + */ +ol.style.Circle.prototype.getRadius = function() { + return this.radius_; +}; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.getSize = function() { + return this.size_; +}; + + +/** + * Get the stroke style for the circle. + * @return {ol.style.Stroke} Stroke style. + * @api + */ +ol.style.Circle.prototype.getStroke = function() { + return this.stroke_; +}; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.listenImageChange = ol.nullFunction; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.load = ol.nullFunction; + + +/** + * @inheritDoc + */ +ol.style.Circle.prototype.unlistenImageChange = ol.nullFunction; + + +/** + * @private + * @param {ol.style.AtlasManager|undefined} atlasManager An atlas manager. + */ +ol.style.Circle.prototype.render_ = function(atlasManager) { + var imageSize; + var lineDash = null; + var strokeStyle; + var strokeWidth = 0; + + if (this.stroke_) { + strokeStyle = ol.color.asString(this.stroke_.getColor()); + strokeWidth = this.stroke_.getWidth(); + if (strokeWidth === undefined) { + strokeWidth = ol.render.canvas.defaultLineWidth; + } + lineDash = this.stroke_.getLineDash(); + if (!ol.has.CANVAS_LINE_DASH) { + lineDash = null; + } + } + + + var size = 2 * (this.radius_ + strokeWidth) + 1; + + /** @type {ol.CircleRenderOptions} */ + var renderOptions = { + strokeStyle: strokeStyle, + strokeWidth: strokeWidth, + size: size, + lineDash: lineDash + }; + + if (atlasManager === undefined) { + // no atlas manager is used, create a new canvas + var context = ol.dom.createCanvasContext2D(size, size); + this.canvas_ = context.canvas; + + // canvas.width and height are rounded to the closest integer + size = this.canvas_.width; + imageSize = size; + + // draw the circle on the canvas + this.draw_(renderOptions, context, 0, 0); + + this.createHitDetectionCanvas_(renderOptions); + } else { + // an atlas manager is used, add the symbol to an atlas + size = Math.round(size); + + var hasCustomHitDetectionImage = !this.fill_; + var renderHitDetectionCallback; + if (hasCustomHitDetectionImage) { + // render the hit-detection image into a separate atlas image + renderHitDetectionCallback = + this.drawHitDetectionCanvas_.bind(this, renderOptions); + } + + var id = this.getChecksum(); + var info = atlasManager.add( + id, size, size, this.draw_.bind(this, renderOptions), + renderHitDetectionCallback); + goog.asserts.assert(info, 'circle radius is too large'); + + this.canvas_ = info.image; + this.origin_ = [info.offsetX, info.offsetY]; + imageSize = info.image.width; + + if (hasCustomHitDetectionImage) { + this.hitDetectionCanvas_ = info.hitImage; + this.hitDetectionImageSize_ = + [info.hitImage.width, info.hitImage.height]; + } else { + this.hitDetectionCanvas_ = this.canvas_; + this.hitDetectionImageSize_ = [imageSize, imageSize]; + } + } + + this.anchor_ = [size / 2, size / 2]; + this.size_ = [size, size]; + this.imageSize_ = [imageSize, imageSize]; +}; + + +/** + * @private + * @param {ol.CircleRenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The rendering context. + * @param {number} x The origin for the symbol (x). + * @param {number} y The origin for the symbol (y). + */ +ol.style.Circle.prototype.draw_ = function(renderOptions, context, x, y) { + // reset transform + context.setTransform(1, 0, 0, 1, 0, 0); + + // then move to (x, y) + context.translate(x, y); + + context.beginPath(); + context.arc( + renderOptions.size / 2, renderOptions.size / 2, + this.radius_, 0, 2 * Math.PI, true); + + if (this.fill_) { + context.fillStyle = ol.colorlike.asColorLike(this.fill_.getColor()); + context.fill(); + } + if (this.stroke_) { + context.strokeStyle = renderOptions.strokeStyle; + context.lineWidth = renderOptions.strokeWidth; + if (renderOptions.lineDash) { + context.setLineDash(renderOptions.lineDash); + } + context.stroke(); + } + context.closePath(); +}; + + +/** + * @private + * @param {ol.CircleRenderOptions} renderOptions Render options. + */ +ol.style.Circle.prototype.createHitDetectionCanvas_ = function(renderOptions) { + this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size]; + if (this.fill_) { + this.hitDetectionCanvas_ = this.canvas_; + return; + } + + // if no fill style is set, create an extra hit-detection image with a + // default fill style + var context = ol.dom.createCanvasContext2D(renderOptions.size, renderOptions.size); + this.hitDetectionCanvas_ = context.canvas; + + this.drawHitDetectionCanvas_(renderOptions, context, 0, 0); +}; + + +/** + * @private + * @param {ol.CircleRenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The context. + * @param {number} x The origin for the symbol (x). + * @param {number} y The origin for the symbol (y). + */ +ol.style.Circle.prototype.drawHitDetectionCanvas_ = function(renderOptions, context, x, y) { + // reset transform + context.setTransform(1, 0, 0, 1, 0, 0); + + // then move to (x, y) + context.translate(x, y); + + context.beginPath(); + context.arc( + renderOptions.size / 2, renderOptions.size / 2, + this.radius_, 0, 2 * Math.PI, true); + + context.fillStyle = ol.color.asString(ol.render.canvas.defaultFillStyle); + context.fill(); + if (this.stroke_) { + context.strokeStyle = renderOptions.strokeStyle; + context.lineWidth = renderOptions.strokeWidth; + if (renderOptions.lineDash) { + context.setLineDash(renderOptions.lineDash); + } + context.stroke(); + } + context.closePath(); +}; + + +/** + * @return {string} The checksum. + */ +ol.style.Circle.prototype.getChecksum = function() { + var strokeChecksum = this.stroke_ ? + this.stroke_.getChecksum() : '-'; + var fillChecksum = this.fill_ ? + this.fill_.getChecksum() : '-'; + + var recalculate = !this.checksums_ || + (strokeChecksum != this.checksums_[1] || + fillChecksum != this.checksums_[2] || + this.radius_ != this.checksums_[3]); + + if (recalculate) { + var checksum = 'c' + strokeChecksum + fillChecksum + + (this.radius_ !== undefined ? this.radius_.toString() : '-'); + this.checksums_ = [checksum, strokeChecksum, fillChecksum, this.radius_]; + } + + return this.checksums_[0]; +}; + +goog.provide('ol.style.Style'); +goog.provide('ol.style.defaultGeometryFunction'); + +goog.require('goog.asserts'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.style.Circle'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Image'); +goog.require('ol.style.Stroke'); + + +/** + * @classdesc + * Container for vector feature rendering styles. Any changes made to the style + * or its children through `set*()` methods will not take effect until the + * feature or layer that uses the style is re-rendered. + * + * @constructor + * @struct + * @param {olx.style.StyleOptions=} opt_options Style options. + * @api + */ +ol.style.Style = function(opt_options) { + + var options = opt_options || {}; + + /** + * @private + * @type {string|ol.geom.Geometry|ol.StyleGeometryFunction} + */ + this.geometry_ = null; + + /** + * @private + * @type {!ol.StyleGeometryFunction} + */ + this.geometryFunction_ = ol.style.defaultGeometryFunction; + + if (options.geometry !== undefined) { + this.setGeometry(options.geometry); + } + + /** + * @private + * @type {ol.style.Fill} + */ + this.fill_ = options.fill !== undefined ? options.fill : null; + + /** + * @private + * @type {ol.style.Image} + */ + this.image_ = options.image !== undefined ? options.image : null; + + /** + * @private + * @type {ol.style.Stroke} + */ + this.stroke_ = options.stroke !== undefined ? options.stroke : null; + + /** + * @private + * @type {ol.style.Text} + */ + this.text_ = options.text !== undefined ? options.text : null; + + /** + * @private + * @type {number|undefined} + */ + this.zIndex_ = options.zIndex; + +}; + + +/** + * Get the geometry to be rendered. + * @return {string|ol.geom.Geometry|ol.StyleGeometryFunction} + * Feature property or geometry or function that returns the geometry that will + * be rendered with this style. + * @api + */ +ol.style.Style.prototype.getGeometry = function() { + return this.geometry_; +}; + + +/** + * Get the function used to generate a geometry for rendering. + * @return {!ol.StyleGeometryFunction} Function that is called with a feature + * and returns the geometry to render instead of the feature's geometry. + * @api + */ +ol.style.Style.prototype.getGeometryFunction = function() { + return this.geometryFunction_; +}; + + +/** + * Get the fill style. + * @return {ol.style.Fill} Fill style. + * @api + */ +ol.style.Style.prototype.getFill = function() { + return this.fill_; +}; + + +/** + * Get the image style. + * @return {ol.style.Image} Image style. + * @api + */ +ol.style.Style.prototype.getImage = function() { + return this.image_; +}; + + +/** + * Get the stroke style. + * @return {ol.style.Stroke} Stroke style. + * @api + */ +ol.style.Style.prototype.getStroke = function() { + return this.stroke_; +}; + + +/** + * Get the text style. + * @return {ol.style.Text} Text style. + * @api + */ +ol.style.Style.prototype.getText = function() { + return this.text_; +}; + + +/** + * Get the z-index for the style. + * @return {number|undefined} ZIndex. + * @api + */ +ol.style.Style.prototype.getZIndex = function() { + return this.zIndex_; +}; + + +/** + * Set a geometry that is rendered instead of the feature's geometry. + * + * @param {string|ol.geom.Geometry|ol.StyleGeometryFunction} geometry + * Feature property or geometry or function returning a geometry to render + * for this style. + * @api + */ +ol.style.Style.prototype.setGeometry = function(geometry) { + if (typeof geometry === 'function') { + this.geometryFunction_ = geometry; + } else if (typeof geometry === 'string') { + this.geometryFunction_ = function(feature) { + var result = feature.get(geometry); + if (result) { + goog.asserts.assertInstanceof(result, ol.geom.Geometry, + 'feature geometry must be an ol.geom.Geometry instance'); + } + return result; + }; + } else if (!geometry) { + this.geometryFunction_ = ol.style.defaultGeometryFunction; + } else if (geometry !== undefined) { + goog.asserts.assertInstanceof(geometry, ol.geom.Geometry, + 'geometry must be an ol.geom.Geometry instance'); + this.geometryFunction_ = function() { + return geometry; + }; + } + this.geometry_ = geometry; +}; + + +/** + * Set the z-index. + * + * @param {number|undefined} zIndex ZIndex. + * @api + */ +ol.style.Style.prototype.setZIndex = function(zIndex) { + this.zIndex_ = zIndex; +}; + + +/** + * Convert the provided object into a style function. Functions passed through + * unchanged. Arrays of ol.style.Style or single style objects wrapped in a + * new style function. + * @param {ol.StyleFunction|Array.<ol.style.Style>|ol.style.Style} obj + * A style function, a single style, or an array of styles. + * @return {ol.StyleFunction} A style function. + */ +ol.style.createStyleFunction = function(obj) { + var styleFunction; + + if (typeof obj === 'function') { + styleFunction = obj; + } else { + /** + * @type {Array.<ol.style.Style>} + */ + var styles; + if (Array.isArray(obj)) { + styles = obj; + } else { + goog.asserts.assertInstanceof(obj, ol.style.Style, + 'obj geometry must be an ol.style.Style instance'); + styles = [obj]; + } + styleFunction = function() { + return styles; + }; + } + return styleFunction; +}; + + +/** + * @type {Array.<ol.style.Style>} + * @private + */ +ol.style.defaultStyle_ = null; + + +/** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {number} resolution Resolution. + * @return {Array.<ol.style.Style>} Style. + */ +ol.style.defaultStyleFunction = function(feature, resolution) { + // We don't use an immediately-invoked function + // and a closure so we don't get an error at script evaluation time in + // browsers that do not support Canvas. (ol.style.Circle does + // canvas.getContext('2d') at construction time, which will cause an.error + // in such browsers.) + if (!ol.style.defaultStyle_) { + var fill = new ol.style.Fill({ + color: 'rgba(255,255,255,0.4)' + }); + var stroke = new ol.style.Stroke({ + color: '#3399CC', + width: 1.25 + }); + ol.style.defaultStyle_ = [ + new ol.style.Style({ + image: new ol.style.Circle({ + fill: fill, + stroke: stroke, + radius: 5 + }), + fill: fill, + stroke: stroke + }) + ]; + } + return ol.style.defaultStyle_; +}; + + +/** + * Default styles for editing features. + * @return {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} Styles + */ +ol.style.createDefaultEditingStyles = function() { + /** @type {Object.<ol.geom.GeometryType, Array.<ol.style.Style>>} */ + var styles = {}; + var white = [255, 255, 255, 1]; + var blue = [0, 153, 255, 1]; + var width = 3; + styles[ol.geom.GeometryType.POLYGON] = [ + new ol.style.Style({ + fill: new ol.style.Fill({ + color: [255, 255, 255, 0.5] + }) + }) + ]; + styles[ol.geom.GeometryType.MULTI_POLYGON] = + styles[ol.geom.GeometryType.POLYGON]; + + styles[ol.geom.GeometryType.LINE_STRING] = [ + new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: white, + width: width + 2 + }) + }), + new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: blue, + width: width + }) + }) + ]; + styles[ol.geom.GeometryType.MULTI_LINE_STRING] = + styles[ol.geom.GeometryType.LINE_STRING]; + + styles[ol.geom.GeometryType.CIRCLE] = + styles[ol.geom.GeometryType.POLYGON].concat( + styles[ol.geom.GeometryType.LINE_STRING] + ); + + + styles[ol.geom.GeometryType.POINT] = [ + new ol.style.Style({ + image: new ol.style.Circle({ + radius: width * 2, + fill: new ol.style.Fill({ + color: blue + }), + stroke: new ol.style.Stroke({ + color: white, + width: width / 2 + }) + }), + zIndex: Infinity + }) + ]; + styles[ol.geom.GeometryType.MULTI_POINT] = + styles[ol.geom.GeometryType.POINT]; + + styles[ol.geom.GeometryType.GEOMETRY_COLLECTION] = + styles[ol.geom.GeometryType.POLYGON].concat( + styles[ol.geom.GeometryType.LINE_STRING], + styles[ol.geom.GeometryType.POINT] + ); + + return styles; +}; + + +/** + * Function that is called with a feature and returns its default geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature to get the geometry + * for. + * @return {ol.geom.Geometry|ol.render.Feature|undefined} Geometry to render. + */ +ol.style.defaultGeometryFunction = function(feature) { + goog.asserts.assert(feature, 'feature must not be null'); + return feature.getGeometry(); +}; + +goog.provide('ol.layer.Vector'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.layer.Layer'); +goog.require('ol.object'); +goog.require('ol.style.Style'); + + +/** + * @enum {string} + */ +ol.layer.VectorProperty = { + RENDER_ORDER: 'renderOrder' +}; + + +/** + * @classdesc + * Vector data that is rendered client-side. + * Note that any property set in the options is set as a {@link ol.Object} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {ol.layer.Layer} + * @fires ol.render.Event + * @param {olx.layer.VectorOptions=} opt_options Options. + * @api stable + */ +ol.layer.Vector = function(opt_options) { + + var options = opt_options ? + opt_options : /** @type {olx.layer.VectorOptions} */ ({}); + + goog.asserts.assert( + options.renderOrder === undefined || !options.renderOrder || + typeof options.renderOrder === 'function', + 'renderOrder must be a comparator function'); + + var baseOptions = ol.object.assign({}, options); + + delete baseOptions.style; + delete baseOptions.renderBuffer; + delete baseOptions.updateWhileAnimating; + delete baseOptions.updateWhileInteracting; + ol.layer.Layer.call(this, /** @type {olx.layer.LayerOptions} */ (baseOptions)); + + /** + * @type {number} + * @private + */ + this.renderBuffer_ = options.renderBuffer !== undefined ? + options.renderBuffer : 100; + + /** + * User provided style. + * @type {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} + * @private + */ + this.style_ = null; + + /** + * Style function for use within the library. + * @type {ol.StyleFunction|undefined} + * @private + */ + this.styleFunction_ = undefined; + + this.setStyle(options.style); + + /** + * @type {boolean} + * @private + */ + this.updateWhileAnimating_ = options.updateWhileAnimating !== undefined ? + options.updateWhileAnimating : false; + + /** + * @type {boolean} + * @private + */ + this.updateWhileInteracting_ = options.updateWhileInteracting !== undefined ? + options.updateWhileInteracting : false; + +}; +ol.inherits(ol.layer.Vector, ol.layer.Layer); + + +/** + * @return {number|undefined} Render buffer. + */ +ol.layer.Vector.prototype.getRenderBuffer = function() { + return this.renderBuffer_; +}; + + +/** + * @return {function(ol.Feature, ol.Feature): number|null|undefined} Render + * order. + */ +ol.layer.Vector.prototype.getRenderOrder = function() { + return /** @type {function(ol.Feature, ol.Feature):number|null|undefined} */ ( + this.get(ol.layer.VectorProperty.RENDER_ORDER)); +}; + + +/** + * Return the associated {@link ol.source.Vector vectorsource} of the layer. + * @function + * @return {ol.source.Vector} Source. + * @api stable + */ +ol.layer.Vector.prototype.getSource; + + +/** + * Get the style for features. This returns whatever was passed to the `style` + * option at construction or to the `setStyle` method. + * @return {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} + * Layer style. + * @api stable + */ +ol.layer.Vector.prototype.getStyle = function() { + return this.style_; +}; + + +/** + * Get the style function. + * @return {ol.StyleFunction|undefined} Layer style function. + * @api stable + */ +ol.layer.Vector.prototype.getStyleFunction = function() { + return this.styleFunction_; +}; + + +/** + * @return {boolean} Whether the rendered layer should be updated while + * animating. + */ +ol.layer.Vector.prototype.getUpdateWhileAnimating = function() { + return this.updateWhileAnimating_; +}; + + +/** + * @return {boolean} Whether the rendered layer should be updated while + * interacting. + */ +ol.layer.Vector.prototype.getUpdateWhileInteracting = function() { + return this.updateWhileInteracting_; +}; + + +/** + * @param {function(ol.Feature, ol.Feature):number|null|undefined} renderOrder + * Render order. + */ +ol.layer.Vector.prototype.setRenderOrder = function(renderOrder) { + goog.asserts.assert( + renderOrder === undefined || !renderOrder || + typeof renderOrder === 'function', + 'renderOrder must be a comparator function'); + this.set(ol.layer.VectorProperty.RENDER_ORDER, renderOrder); +}; + + +/** + * Set the style for features. This can be a single style object, an array + * of styles, or a function that takes a feature and resolution and returns + * an array of styles. If it is `undefined` the default style is used. If + * it is `null` the layer has no style (a `null` style), so only features + * that have their own styles will be rendered in the layer. See + * {@link ol.style} for information on the default style. + * @param {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction|null|undefined} + * style Layer style. + * @api stable + */ +ol.layer.Vector.prototype.setStyle = function(style) { + this.style_ = style !== undefined ? style : ol.style.defaultStyleFunction; + this.styleFunction_ = style === null ? + undefined : ol.style.createStyleFunction(this.style_); + this.changed(); +}; + +goog.provide('ol.layer.VectorTile'); + +goog.require('goog.asserts'); +goog.require('ol.layer.Vector'); +goog.require('ol.object'); + + +/** + * @enum {string} + */ +ol.layer.VectorTileProperty = { + PRELOAD: 'preload', + USE_INTERIM_TILES_ON_ERROR: 'useInterimTilesOnError' +}; + + +/** + * @enum {string} + * Render mode for vector tiles: + * * `'image'`: Vector tiles are rendered as images. Great performance, but + * point symbols and texts are always rotated with the view and pixels are + * scaled during zoom animations. + * * `'hybrid'`: Polygon and line elements are rendered as images, so pixels + * are scaled during zoom animations. Point symbols and texts are accurately + * rendered as vectors and can stay upright on rotated views. + * * `'vector'`: Vector tiles are rendered as vectors. Most accurate rendering + * even during animations, but slower performance than the other options. + * @api + */ +ol.layer.VectorTileRenderType = { + IMAGE: 'image', + HYBRID: 'hybrid', + VECTOR: 'vector' +}; + + +/** + * @classdesc + * Layer for vector tile data that is rendered client-side. + * Note that any property set in the options is set as a {@link ol.Object} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {ol.layer.Vector} + * @param {olx.layer.VectorTileOptions=} opt_options Options. + * @api + */ +ol.layer.VectorTile = function(opt_options) { + var options = opt_options ? opt_options : {}; + + var baseOptions = ol.object.assign({}, options); + + delete baseOptions.preload; + delete baseOptions.useInterimTilesOnError; + ol.layer.Vector.call(this, /** @type {olx.layer.VectorOptions} */ (baseOptions)); + + this.setPreload(options.preload ? options.preload : 0); + this.setUseInterimTilesOnError(options.useInterimTilesOnError ? + options.useInterimTilesOnError : true); + + goog.asserts.assert(options.renderMode == undefined || + options.renderMode == ol.layer.VectorTileRenderType.IMAGE || + options.renderMode == ol.layer.VectorTileRenderType.HYBRID || + options.renderMode == ol.layer.VectorTileRenderType.VECTOR, + 'renderMode needs to be \'image\', \'hybrid\' or \'vector\''); + + /** + * @private + * @type {ol.layer.VectorTileRenderType|string} + */ + this.renderMode_ = options.renderMode || ol.layer.VectorTileRenderType.HYBRID; + +}; +ol.inherits(ol.layer.VectorTile, ol.layer.Vector); + + +/** + * Return the level as number to which we will preload tiles up to. + * @return {number} The level to preload tiles up to. + * @observable + * @api + */ +ol.layer.VectorTile.prototype.getPreload = function() { + return /** @type {number} */ (this.get(ol.layer.VectorTileProperty.PRELOAD)); +}; + + +/** + * @return {ol.layer.VectorTileRenderType|string} The render mode. + */ +ol.layer.VectorTile.prototype.getRenderMode = function() { + return this.renderMode_; +}; + + +/** + * Whether we use interim tiles on error. + * @return {boolean} Use interim tiles on error. + * @observable + * @api + */ +ol.layer.VectorTile.prototype.getUseInterimTilesOnError = function() { + return /** @type {boolean} */ ( + this.get(ol.layer.VectorTileProperty.USE_INTERIM_TILES_ON_ERROR)); +}; + + +/** + * Set the level as number to which we will preload tiles up to. + * @param {number} preload The level to preload tiles up to. + * @observable + * @api + */ +ol.layer.VectorTile.prototype.setPreload = function(preload) { + this.set(ol.layer.TileProperty.PRELOAD, preload); +}; + + +/** + * Set whether we use interim tiles on error. + * @param {boolean} useInterimTilesOnError Use interim tiles on error. + * @observable + * @api + */ +ol.layer.VectorTile.prototype.setUseInterimTilesOnError = function(useInterimTilesOnError) { + this.set( + ol.layer.TileProperty.USE_INTERIM_TILES_ON_ERROR, useInterimTilesOnError); +}; + +// FIXME test, especially polygons with holes and multipolygons +// FIXME need to handle large thick features (where pixel size matters) +// FIXME add offset and end to ol.geom.flat.transform.transform2D? + +goog.provide('ol.render.canvas.Immediate'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol.array'); +goog.require('ol.color'); +goog.require('ol.colorlike'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.flat.transform'); +goog.require('ol.has'); +goog.require('ol.render.VectorContext'); +goog.require('ol.render.canvas'); +goog.require('ol.vec.Mat4'); + + +/** + * @classdesc + * A concrete subclass of {@link ol.render.VectorContext} that implements + * direct rendering of features and geometries to an HTML5 Canvas context. + * Instances of this class are created internally by the library and + * provided to application code as vectorContext member of the + * {@link ol.render.Event} object associated with postcompose, precompose and + * render events emitted by layers and maps. + * + * @constructor + * @extends {ol.render.VectorContext} + * @param {CanvasRenderingContext2D} context Context. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.Extent} extent Extent. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {number} viewRotation View rotation. + * @struct + */ +ol.render.canvas.Immediate = function(context, pixelRatio, extent, transform, viewRotation) { + ol.render.VectorContext.call(this); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = context; + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; + + /** + * @private + * @type {ol.Extent} + */ + this.extent_ = extent; + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.transform_ = transform; + + /** + * @private + * @type {number} + */ + this.viewRotation_ = viewRotation; + + /** + * @private + * @type {?ol.CanvasFillState} + */ + this.contextFillState_ = null; + + /** + * @private + * @type {?ol.CanvasStrokeState} + */ + this.contextStrokeState_ = null; + + /** + * @private + * @type {?ol.CanvasTextState} + */ + this.contextTextState_ = null; + + /** + * @private + * @type {?ol.CanvasFillState} + */ + this.fillState_ = null; + + /** + * @private + * @type {?ol.CanvasStrokeState} + */ + this.strokeState_ = null; + + /** + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|Image} + */ + this.image_ = null; + + /** + * @private + * @type {number} + */ + this.imageAnchorX_ = 0; + + /** + * @private + * @type {number} + */ + this.imageAnchorY_ = 0; + + /** + * @private + * @type {number} + */ + this.imageHeight_ = 0; + + /** + * @private + * @type {number} + */ + this.imageOpacity_ = 0; + + /** + * @private + * @type {number} + */ + this.imageOriginX_ = 0; + + /** + * @private + * @type {number} + */ + this.imageOriginY_ = 0; + + /** + * @private + * @type {boolean} + */ + this.imageRotateWithView_ = false; + + /** + * @private + * @type {number} + */ + this.imageRotation_ = 0; + + /** + * @private + * @type {number} + */ + this.imageScale_ = 0; + + /** + * @private + * @type {boolean} + */ + this.imageSnapToPixel_ = false; + + /** + * @private + * @type {number} + */ + this.imageWidth_ = 0; + + /** + * @private + * @type {string} + */ + this.text_ = ''; + + /** + * @private + * @type {number} + */ + this.textOffsetX_ = 0; + + /** + * @private + * @type {number} + */ + this.textOffsetY_ = 0; + + /** + * @private + * @type {number} + */ + this.textRotation_ = 0; + + /** + * @private + * @type {number} + */ + this.textScale_ = 0; + + /** + * @private + * @type {?ol.CanvasFillState} + */ + this.textFillState_ = null; + + /** + * @private + * @type {?ol.CanvasStrokeState} + */ + this.textStrokeState_ = null; + + /** + * @private + * @type {?ol.CanvasTextState} + */ + this.textState_ = null; + + /** + * @private + * @type {Array.<number>} + */ + this.pixelCoordinates_ = []; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.tmpLocalTransform_ = goog.vec.Mat4.createNumber(); + +}; +ol.inherits(ol.render.canvas.Immediate, ol.render.VectorContext); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @private + */ +ol.render.canvas.Immediate.prototype.drawImages_ = function(flatCoordinates, offset, end, stride) { + if (!this.image_) { + return; + } + goog.asserts.assert(offset === 0, 'offset should be 0'); + goog.asserts.assert(end == flatCoordinates.length, + 'end should be equal to the length of flatCoordinates'); + var pixelCoordinates = ol.geom.flat.transform.transform2D( + flatCoordinates, offset, end, 2, this.transform_, + this.pixelCoordinates_); + var context = this.context_; + var localTransform = this.tmpLocalTransform_; + var alpha = context.globalAlpha; + if (this.imageOpacity_ != 1) { + context.globalAlpha = alpha * this.imageOpacity_; + } + var rotation = this.imageRotation_; + if (this.imageRotateWithView_) { + rotation += this.viewRotation_; + } + var i, ii; + for (i = 0, ii = pixelCoordinates.length; i < ii; i += 2) { + var x = pixelCoordinates[i] - this.imageAnchorX_; + var y = pixelCoordinates[i + 1] - this.imageAnchorY_; + if (this.imageSnapToPixel_) { + x = Math.round(x); + y = Math.round(y); + } + if (rotation !== 0 || this.imageScale_ != 1) { + var centerX = x + this.imageAnchorX_; + var centerY = y + this.imageAnchorY_; + ol.vec.Mat4.makeTransform2D(localTransform, + centerX, centerY, this.imageScale_, this.imageScale_, + rotation, -centerX, -centerY); + context.setTransform( + goog.vec.Mat4.getElement(localTransform, 0, 0), + goog.vec.Mat4.getElement(localTransform, 1, 0), + goog.vec.Mat4.getElement(localTransform, 0, 1), + goog.vec.Mat4.getElement(localTransform, 1, 1), + goog.vec.Mat4.getElement(localTransform, 0, 3), + goog.vec.Mat4.getElement(localTransform, 1, 3)); + } + context.drawImage(this.image_, this.imageOriginX_, this.imageOriginY_, + this.imageWidth_, this.imageHeight_, x, y, + this.imageWidth_, this.imageHeight_); + } + if (rotation !== 0 || this.imageScale_ != 1) { + context.setTransform(1, 0, 0, 1, 0, 0); + } + if (this.imageOpacity_ != 1) { + context.globalAlpha = alpha; + } +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @private + */ +ol.render.canvas.Immediate.prototype.drawText_ = function(flatCoordinates, offset, end, stride) { + if (!this.textState_ || this.text_ === '') { + return; + } + if (this.textFillState_) { + this.setContextFillState_(this.textFillState_); + } + if (this.textStrokeState_) { + this.setContextStrokeState_(this.textStrokeState_); + } + this.setContextTextState_(this.textState_); + goog.asserts.assert(offset === 0, 'offset should be 0'); + goog.asserts.assert(end == flatCoordinates.length, + 'end should be equal to the length of flatCoordinates'); + var pixelCoordinates = ol.geom.flat.transform.transform2D( + flatCoordinates, offset, end, stride, this.transform_, + this.pixelCoordinates_); + var context = this.context_; + for (; offset < end; offset += stride) { + var x = pixelCoordinates[offset] + this.textOffsetX_; + var y = pixelCoordinates[offset + 1] + this.textOffsetY_; + if (this.textRotation_ !== 0 || this.textScale_ != 1) { + var localTransform = ol.vec.Mat4.makeTransform2D(this.tmpLocalTransform_, + x, y, this.textScale_, this.textScale_, this.textRotation_, -x, -y); + context.setTransform( + goog.vec.Mat4.getElement(localTransform, 0, 0), + goog.vec.Mat4.getElement(localTransform, 1, 0), + goog.vec.Mat4.getElement(localTransform, 0, 1), + goog.vec.Mat4.getElement(localTransform, 1, 1), + goog.vec.Mat4.getElement(localTransform, 0, 3), + goog.vec.Mat4.getElement(localTransform, 1, 3)); + } + if (this.textStrokeState_) { + context.strokeText(this.text_, x, y); + } + if (this.textFillState_) { + context.fillText(this.text_, x, y); + } + } + if (this.textRotation_ !== 0 || this.textScale_ != 1) { + context.setTransform(1, 0, 0, 1, 0, 0); + } +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {boolean} close Close. + * @private + * @return {number} end End. + */ +ol.render.canvas.Immediate.prototype.moveToLineTo_ = function(flatCoordinates, offset, end, stride, close) { + var context = this.context_; + var pixelCoordinates = ol.geom.flat.transform.transform2D( + flatCoordinates, offset, end, stride, this.transform_, + this.pixelCoordinates_); + context.moveTo(pixelCoordinates[0], pixelCoordinates[1]); + var length = pixelCoordinates.length; + if (close) { + length -= 2; + } + for (var i = 2; i < length; i += 2) { + context.lineTo(pixelCoordinates[i], pixelCoordinates[i + 1]); + } + if (close) { + context.closePath(); + } + return end; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @private + * @return {number} End. + */ +ol.render.canvas.Immediate.prototype.drawRings_ = function(flatCoordinates, offset, ends, stride) { + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + offset = this.moveToLineTo_( + flatCoordinates, offset, ends[i], stride, true); + } + return offset; +}; + + +/** + * Render a circle geometry into the canvas. Rendering is immediate and uses + * the current fill and stroke styles. + * + * @param {ol.geom.Circle} geometry Circle geometry. + * @api + */ +ol.render.canvas.Immediate.prototype.drawCircle = function(geometry) { + if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.fillState_ || this.strokeState_) { + if (this.fillState_) { + this.setContextFillState_(this.fillState_); + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + } + var pixelCoordinates = ol.geom.transformSimpleGeometry2D( + geometry, this.transform_, this.pixelCoordinates_); + var dx = pixelCoordinates[2] - pixelCoordinates[0]; + var dy = pixelCoordinates[3] - pixelCoordinates[1]; + var radius = Math.sqrt(dx * dx + dy * dy); + var context = this.context_; + context.beginPath(); + context.arc( + pixelCoordinates[0], pixelCoordinates[1], radius, 0, 2 * Math.PI); + if (this.fillState_) { + context.fill(); + } + if (this.strokeState_) { + context.stroke(); + } + } + if (this.text_ !== '') { + this.drawText_(geometry.getCenter(), 0, 2, 2); + } +}; + + +/** + * Set the rendering style. Note that since this is an immediate rendering API, + * any `zIndex` on the provided style will be ignored. + * + * @param {ol.style.Style} style The rendering style. + * @api + */ +ol.render.canvas.Immediate.prototype.setStyle = function(style) { + this.setFillStrokeStyle(style.getFill(), style.getStroke()); + this.setImageStyle(style.getImage()); + this.setTextStyle(style.getText()); +}; + + +/** + * Render a geometry into the canvas. Call + * {@link ol.render.canvas.Immediate#setStyle} first to set the rendering style. + * + * @param {ol.geom.Geometry|ol.render.Feature} geometry The geometry to render. + * @api + */ +ol.render.canvas.Immediate.prototype.drawGeometry = function(geometry) { + var type = geometry.getType(); + switch (type) { + case ol.geom.GeometryType.POINT: + this.drawPoint(/** @type {ol.geom.Point} */ (geometry)); + break; + case ol.geom.GeometryType.LINE_STRING: + this.drawLineString(/** @type {ol.geom.LineString} */ (geometry)); + break; + case ol.geom.GeometryType.POLYGON: + this.drawPolygon(/** @type {ol.geom.Polygon} */ (geometry)); + break; + case ol.geom.GeometryType.MULTI_POINT: + this.drawMultiPoint(/** @type {ol.geom.MultiPoint} */ (geometry)); + break; + case ol.geom.GeometryType.MULTI_LINE_STRING: + this.drawMultiLineString(/** @type {ol.geom.MultiLineString} */ (geometry)); + break; + case ol.geom.GeometryType.MULTI_POLYGON: + this.drawMultiPolygon(/** @type {ol.geom.MultiPolygon} */ (geometry)); + break; + case ol.geom.GeometryType.GEOMETRY_COLLECTION: + this.drawGeometryCollection(/** @type {ol.geom.GeometryCollection} */ (geometry)); + break; + case ol.geom.GeometryType.CIRCLE: + this.drawCircle(/** @type {ol.geom.Circle} */ (geometry)); + break; + default: + goog.asserts.fail('Unsupported geometry type: ' + type); + } +}; + + +/** + * Render a feature into the canvas. Note that any `zIndex` on the provided + * style will be ignored - features are rendered immediately in the order that + * this method is called. If you need `zIndex` support, you should be using an + * {@link ol.layer.Vector} instead. + * + * @param {ol.Feature} feature Feature. + * @param {ol.style.Style} style Style. + * @api + */ +ol.render.canvas.Immediate.prototype.drawFeature = function(feature, style) { + var geometry = style.getGeometryFunction()(feature); + if (!geometry || + !ol.extent.intersects(this.extent_, geometry.getExtent())) { + return; + } + this.setStyle(style); + goog.asserts.assert(geometry, 'geometry must be truthy'); + this.drawGeometry(geometry); +}; + + +/** + * Render a GeometryCollection to the canvas. Rendering is immediate and + * uses the current styles appropriate for each geometry in the collection. + * + * @param {ol.geom.GeometryCollection} geometry Geometry collection. + */ +ol.render.canvas.Immediate.prototype.drawGeometryCollection = function(geometry) { + var geometries = geometry.getGeometriesArray(); + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + this.drawGeometry(geometries[i]); + } +}; + + +/** + * Render a Point geometry into the canvas. Rendering is immediate and uses + * the current style. + * + * @param {ol.geom.Point|ol.render.Feature} geometry Point geometry. + */ +ol.render.canvas.Immediate.prototype.drawPoint = function(geometry) { + var flatCoordinates = geometry.getFlatCoordinates(); + var stride = geometry.getStride(); + if (this.image_) { + this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); + } + if (this.text_ !== '') { + this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); + } +}; + + +/** + * Render a MultiPoint geometry into the canvas. Rendering is immediate and + * uses the current style. + * + * @param {ol.geom.MultiPoint|ol.render.Feature} geometry MultiPoint geometry. + */ +ol.render.canvas.Immediate.prototype.drawMultiPoint = function(geometry) { + var flatCoordinates = geometry.getFlatCoordinates(); + var stride = geometry.getStride(); + if (this.image_) { + this.drawImages_(flatCoordinates, 0, flatCoordinates.length, stride); + } + if (this.text_ !== '') { + this.drawText_(flatCoordinates, 0, flatCoordinates.length, stride); + } +}; + + +/** + * Render a LineString into the canvas. Rendering is immediate and uses + * the current style. + * + * @param {ol.geom.LineString|ol.render.Feature} geometry LineString geometry. + */ +ol.render.canvas.Immediate.prototype.drawLineString = function(geometry) { + if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + var context = this.context_; + var flatCoordinates = geometry.getFlatCoordinates(); + context.beginPath(); + this.moveToLineTo_(flatCoordinates, 0, flatCoordinates.length, + geometry.getStride(), false); + context.stroke(); + } + if (this.text_ !== '') { + var flatMidpoint = geometry.getFlatMidpoint(); + this.drawText_(flatMidpoint, 0, 2, 2); + } +}; + + +/** + * Render a MultiLineString geometry into the canvas. Rendering is immediate + * and uses the current style. + * + * @param {ol.geom.MultiLineString|ol.render.Feature} geometry MultiLineString + * geometry. + */ +ol.render.canvas.Immediate.prototype.drawMultiLineString = function(geometry) { + var geometryExtent = geometry.getExtent(); + if (!ol.extent.intersects(this.extent_, geometryExtent)) { + return; + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + var context = this.context_; + var flatCoordinates = geometry.getFlatCoordinates(); + var offset = 0; + var ends = geometry.getEnds(); + var stride = geometry.getStride(); + context.beginPath(); + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + offset = this.moveToLineTo_( + flatCoordinates, offset, ends[i], stride, false); + } + context.stroke(); + } + if (this.text_ !== '') { + var flatMidpoints = geometry.getFlatMidpoints(); + this.drawText_(flatMidpoints, 0, flatMidpoints.length, 2); + } +}; + + +/** + * Render a Polygon geometry into the canvas. Rendering is immediate and uses + * the current style. + * + * @param {ol.geom.Polygon|ol.render.Feature} geometry Polygon geometry. + */ +ol.render.canvas.Immediate.prototype.drawPolygon = function(geometry) { + if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.strokeState_ || this.fillState_) { + if (this.fillState_) { + this.setContextFillState_(this.fillState_); + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + } + var context = this.context_; + context.beginPath(); + this.drawRings_(geometry.getOrientedFlatCoordinates(), + 0, geometry.getEnds(), geometry.getStride()); + if (this.fillState_) { + context.fill(); + } + if (this.strokeState_) { + context.stroke(); + } + } + if (this.text_ !== '') { + var flatInteriorPoint = geometry.getFlatInteriorPoint(); + this.drawText_(flatInteriorPoint, 0, 2, 2); + } +}; + + +/** + * Render MultiPolygon geometry into the canvas. Rendering is immediate and + * uses the current style. + * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry. + */ +ol.render.canvas.Immediate.prototype.drawMultiPolygon = function(geometry) { + if (!ol.extent.intersects(this.extent_, geometry.getExtent())) { + return; + } + if (this.strokeState_ || this.fillState_) { + if (this.fillState_) { + this.setContextFillState_(this.fillState_); + } + if (this.strokeState_) { + this.setContextStrokeState_(this.strokeState_); + } + var context = this.context_; + var flatCoordinates = geometry.getOrientedFlatCoordinates(); + var offset = 0; + var endss = geometry.getEndss(); + var stride = geometry.getStride(); + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + context.beginPath(); + offset = this.drawRings_(flatCoordinates, offset, ends, stride); + if (this.fillState_) { + context.fill(); + } + if (this.strokeState_) { + context.stroke(); + } + } + } + if (this.text_ !== '') { + var flatInteriorPoints = geometry.getFlatInteriorPoints(); + this.drawText_(flatInteriorPoints, 0, flatInteriorPoints.length, 2); + } +}; + + +/** + * @param {ol.CanvasFillState} fillState Fill state. + * @private + */ +ol.render.canvas.Immediate.prototype.setContextFillState_ = function(fillState) { + var context = this.context_; + var contextFillState = this.contextFillState_; + if (!contextFillState) { + context.fillStyle = fillState.fillStyle; + this.contextFillState_ = { + fillStyle: fillState.fillStyle + }; + } else { + if (contextFillState.fillStyle != fillState.fillStyle) { + contextFillState.fillStyle = context.fillStyle = fillState.fillStyle; + } + } +}; + + +/** + * @param {ol.CanvasStrokeState} strokeState Stroke state. + * @private + */ +ol.render.canvas.Immediate.prototype.setContextStrokeState_ = function(strokeState) { + var context = this.context_; + var contextStrokeState = this.contextStrokeState_; + if (!contextStrokeState) { + context.lineCap = strokeState.lineCap; + if (ol.has.CANVAS_LINE_DASH) { + context.setLineDash(strokeState.lineDash); + } + context.lineJoin = strokeState.lineJoin; + context.lineWidth = strokeState.lineWidth; + context.miterLimit = strokeState.miterLimit; + context.strokeStyle = strokeState.strokeStyle; + this.contextStrokeState_ = { + lineCap: strokeState.lineCap, + lineDash: strokeState.lineDash, + lineJoin: strokeState.lineJoin, + lineWidth: strokeState.lineWidth, + miterLimit: strokeState.miterLimit, + strokeStyle: strokeState.strokeStyle + }; + } else { + if (contextStrokeState.lineCap != strokeState.lineCap) { + contextStrokeState.lineCap = context.lineCap = strokeState.lineCap; + } + if (ol.has.CANVAS_LINE_DASH) { + if (!ol.array.equals( + contextStrokeState.lineDash, strokeState.lineDash)) { + context.setLineDash(contextStrokeState.lineDash = strokeState.lineDash); + } + } + if (contextStrokeState.lineJoin != strokeState.lineJoin) { + contextStrokeState.lineJoin = context.lineJoin = strokeState.lineJoin; + } + if (contextStrokeState.lineWidth != strokeState.lineWidth) { + contextStrokeState.lineWidth = context.lineWidth = strokeState.lineWidth; + } + if (contextStrokeState.miterLimit != strokeState.miterLimit) { + contextStrokeState.miterLimit = context.miterLimit = + strokeState.miterLimit; + } + if (contextStrokeState.strokeStyle != strokeState.strokeStyle) { + contextStrokeState.strokeStyle = context.strokeStyle = + strokeState.strokeStyle; + } + } +}; + + +/** + * @param {ol.CanvasTextState} textState Text state. + * @private + */ +ol.render.canvas.Immediate.prototype.setContextTextState_ = function(textState) { + var context = this.context_; + var contextTextState = this.contextTextState_; + if (!contextTextState) { + context.font = textState.font; + context.textAlign = textState.textAlign; + context.textBaseline = textState.textBaseline; + this.contextTextState_ = { + font: textState.font, + textAlign: textState.textAlign, + textBaseline: textState.textBaseline + }; + } else { + if (contextTextState.font != textState.font) { + contextTextState.font = context.font = textState.font; + } + if (contextTextState.textAlign != textState.textAlign) { + contextTextState.textAlign = context.textAlign = textState.textAlign; + } + if (contextTextState.textBaseline != textState.textBaseline) { + contextTextState.textBaseline = context.textBaseline = + textState.textBaseline; + } + } +}; + + +/** + * Set the fill and stroke style for subsequent draw operations. To clear + * either fill or stroke styles, pass null for the appropriate parameter. + * + * @param {ol.style.Fill} fillStyle Fill style. + * @param {ol.style.Stroke} strokeStyle Stroke style. + */ +ol.render.canvas.Immediate.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { + if (!fillStyle) { + this.fillState_ = null; + } else { + var fillStyleColor = fillStyle.getColor(); + this.fillState_ = { + fillStyle: ol.colorlike.asColorLike(fillStyleColor ? + fillStyleColor : ol.render.canvas.defaultFillStyle) + }; + } + if (!strokeStyle) { + this.strokeState_ = null; + } else { + var strokeStyleColor = strokeStyle.getColor(); + var strokeStyleLineCap = strokeStyle.getLineCap(); + var strokeStyleLineDash = strokeStyle.getLineDash(); + var strokeStyleLineJoin = strokeStyle.getLineJoin(); + var strokeStyleWidth = strokeStyle.getWidth(); + var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); + this.strokeState_ = { + lineCap: strokeStyleLineCap !== undefined ? + strokeStyleLineCap : ol.render.canvas.defaultLineCap, + lineDash: strokeStyleLineDash ? + strokeStyleLineDash : ol.render.canvas.defaultLineDash, + lineJoin: strokeStyleLineJoin !== undefined ? + strokeStyleLineJoin : ol.render.canvas.defaultLineJoin, + lineWidth: this.pixelRatio_ * (strokeStyleWidth !== undefined ? + strokeStyleWidth : ol.render.canvas.defaultLineWidth), + miterLimit: strokeStyleMiterLimit !== undefined ? + strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit, + strokeStyle: ol.color.asString(strokeStyleColor ? + strokeStyleColor : ol.render.canvas.defaultStrokeStyle) + }; + } +}; + + +/** + * Set the image style for subsequent draw operations. Pass null to remove + * the image style. + * + * @param {ol.style.Image} imageStyle Image style. + */ +ol.render.canvas.Immediate.prototype.setImageStyle = function(imageStyle) { + if (!imageStyle) { + this.image_ = null; + } else { + var imageAnchor = imageStyle.getAnchor(); + // FIXME pixel ratio + var imageImage = imageStyle.getImage(1); + var imageOrigin = imageStyle.getOrigin(); + var imageSize = imageStyle.getSize(); + goog.asserts.assert(imageAnchor, 'imageAnchor must be truthy'); + goog.asserts.assert(imageImage, 'imageImage must be truthy'); + goog.asserts.assert(imageOrigin, 'imageOrigin must be truthy'); + goog.asserts.assert(imageSize, 'imageSize must be truthy'); + this.imageAnchorX_ = imageAnchor[0]; + this.imageAnchorY_ = imageAnchor[1]; + this.imageHeight_ = imageSize[1]; + this.image_ = imageImage; + this.imageOpacity_ = imageStyle.getOpacity(); + this.imageOriginX_ = imageOrigin[0]; + this.imageOriginY_ = imageOrigin[1]; + this.imageRotateWithView_ = imageStyle.getRotateWithView(); + this.imageRotation_ = imageStyle.getRotation(); + this.imageScale_ = imageStyle.getScale(); + this.imageSnapToPixel_ = imageStyle.getSnapToPixel(); + this.imageWidth_ = imageSize[0]; + } +}; + + +/** + * Set the text style for subsequent draw operations. Pass null to + * remove the text style. + * + * @param {ol.style.Text} textStyle Text style. + */ +ol.render.canvas.Immediate.prototype.setTextStyle = function(textStyle) { + if (!textStyle) { + this.text_ = ''; + } else { + var textFillStyle = textStyle.getFill(); + if (!textFillStyle) { + this.textFillState_ = null; + } else { + var textFillStyleColor = textFillStyle.getColor(); + this.textFillState_ = { + fillStyle: ol.colorlike.asColorLike(textFillStyleColor ? + textFillStyleColor : ol.render.canvas.defaultFillStyle) + }; + } + var textStrokeStyle = textStyle.getStroke(); + if (!textStrokeStyle) { + this.textStrokeState_ = null; + } else { + var textStrokeStyleColor = textStrokeStyle.getColor(); + var textStrokeStyleLineCap = textStrokeStyle.getLineCap(); + var textStrokeStyleLineDash = textStrokeStyle.getLineDash(); + var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin(); + var textStrokeStyleWidth = textStrokeStyle.getWidth(); + var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit(); + this.textStrokeState_ = { + lineCap: textStrokeStyleLineCap !== undefined ? + textStrokeStyleLineCap : ol.render.canvas.defaultLineCap, + lineDash: textStrokeStyleLineDash ? + textStrokeStyleLineDash : ol.render.canvas.defaultLineDash, + lineJoin: textStrokeStyleLineJoin !== undefined ? + textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin, + lineWidth: textStrokeStyleWidth !== undefined ? + textStrokeStyleWidth : ol.render.canvas.defaultLineWidth, + miterLimit: textStrokeStyleMiterLimit !== undefined ? + textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit, + strokeStyle: ol.color.asString(textStrokeStyleColor ? + textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle) + }; + } + var textFont = textStyle.getFont(); + var textOffsetX = textStyle.getOffsetX(); + var textOffsetY = textStyle.getOffsetY(); + var textRotation = textStyle.getRotation(); + var textScale = textStyle.getScale(); + var textText = textStyle.getText(); + var textTextAlign = textStyle.getTextAlign(); + var textTextBaseline = textStyle.getTextBaseline(); + this.textState_ = { + font: textFont !== undefined ? + textFont : ol.render.canvas.defaultFont, + textAlign: textTextAlign !== undefined ? + textTextAlign : ol.render.canvas.defaultTextAlign, + textBaseline: textTextBaseline !== undefined ? + textTextBaseline : ol.render.canvas.defaultTextBaseline + }; + this.text_ = textText !== undefined ? textText : ''; + this.textOffsetX_ = + textOffsetX !== undefined ? (this.pixelRatio_ * textOffsetX) : 0; + this.textOffsetY_ = + textOffsetY !== undefined ? (this.pixelRatio_ * textOffsetY) : 0; + this.textRotation_ = textRotation !== undefined ? textRotation : 0; + this.textScale_ = this.pixelRatio_ * (textScale !== undefined ? + textScale : 1); + } +}; + +goog.provide('ol.renderer.canvas.Layer'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol.extent'); +goog.require('ol.layer.Layer'); +goog.require('ol.render.Event'); +goog.require('ol.render.EventType'); +goog.require('ol.render.canvas'); +goog.require('ol.render.canvas.Immediate'); +goog.require('ol.renderer.Layer'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.Layer} + * @param {ol.layer.Layer} layer Layer. + */ +ol.renderer.canvas.Layer = function(layer) { + + ol.renderer.Layer.call(this, layer); + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + +}; +ol.inherits(ol.renderer.canvas.Layer, ol.renderer.Layer); + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @param {CanvasRenderingContext2D} context Context. + */ +ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerState, context) { + + this.dispatchPreComposeEvent(context, frameState); + + var image = this.getImage(); + if (image) { + + // clipped rendering if layer extent is set + var extent = layerState.extent; + var clipped = extent !== undefined; + if (clipped) { + goog.asserts.assert(extent !== undefined, + 'layerState extent is defined'); + var pixelRatio = frameState.pixelRatio; + var width = frameState.size[0] * pixelRatio; + var height = frameState.size[1] * pixelRatio; + var rotation = frameState.viewState.rotation; + var topLeft = ol.extent.getTopLeft(extent); + var topRight = ol.extent.getTopRight(extent); + var bottomRight = ol.extent.getBottomRight(extent); + var bottomLeft = ol.extent.getBottomLeft(extent); + + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + topLeft, topLeft); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + topRight, topRight); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + bottomRight, bottomRight); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + bottomLeft, bottomLeft); + + context.save(); + ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); + context.beginPath(); + context.moveTo(topLeft[0] * pixelRatio, topLeft[1] * pixelRatio); + context.lineTo(topRight[0] * pixelRatio, topRight[1] * pixelRatio); + context.lineTo(bottomRight[0] * pixelRatio, bottomRight[1] * pixelRatio); + context.lineTo(bottomLeft[0] * pixelRatio, bottomLeft[1] * pixelRatio); + context.clip(); + ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); + } + + var imageTransform = this.getImageTransform(); + // for performance reasons, context.save / context.restore is not used + // to save and restore the transformation matrix and the opacity. + // see http://jsperf.com/context-save-restore-versus-variable + var alpha = context.globalAlpha; + context.globalAlpha = layerState.opacity; + + // for performance reasons, context.setTransform is only used + // when the view is rotated. see http://jsperf.com/canvas-transform + var dx = goog.vec.Mat4.getElement(imageTransform, 0, 3); + var dy = goog.vec.Mat4.getElement(imageTransform, 1, 3); + var dw = image.width * goog.vec.Mat4.getElement(imageTransform, 0, 0); + var dh = image.height * goog.vec.Mat4.getElement(imageTransform, 1, 1); + context.drawImage(image, 0, 0, +image.width, +image.height, + Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh)); + context.globalAlpha = alpha; + + if (clipped) { + context.restore(); + } + } + + this.dispatchPostComposeEvent(context, frameState); + +}; + + +/** + * @param {ol.render.EventType} type Event type. + * @param {CanvasRenderingContext2D} context Context. + * @param {olx.FrameState} frameState Frame state. + * @param {goog.vec.Mat4.Number=} opt_transform Transform. + * @private + */ +ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) { + var layer = this.getLayer(); + if (layer.hasListener(type)) { + var width = frameState.size[0] * frameState.pixelRatio; + var height = frameState.size[1] * frameState.pixelRatio; + var rotation = frameState.viewState.rotation; + ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); + var transform = opt_transform !== undefined ? + opt_transform : this.getTransform(frameState, 0); + var render = new ol.render.canvas.Immediate( + context, frameState.pixelRatio, frameState.extent, transform, + frameState.viewState.rotation); + var composeEvent = new ol.render.Event(type, layer, render, frameState, + context, null); + layer.dispatchEvent(composeEvent); + ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); + } +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {olx.FrameState} frameState Frame state. + * @param {goog.vec.Mat4.Number=} opt_transform Transform. + * @protected + */ +ol.renderer.canvas.Layer.prototype.dispatchPostComposeEvent = function(context, frameState, opt_transform) { + this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, context, + frameState, opt_transform); +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {olx.FrameState} frameState Frame state. + * @param {goog.vec.Mat4.Number=} opt_transform Transform. + * @protected + */ +ol.renderer.canvas.Layer.prototype.dispatchPreComposeEvent = function(context, frameState, opt_transform) { + this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, context, + frameState, opt_transform); +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {olx.FrameState} frameState Frame state. + * @param {goog.vec.Mat4.Number=} opt_transform Transform. + * @protected + */ +ol.renderer.canvas.Layer.prototype.dispatchRenderEvent = function(context, frameState, opt_transform) { + this.dispatchComposeEvent_(ol.render.EventType.RENDER, context, + frameState, opt_transform); +}; + + +/** + * @return {HTMLCanvasElement|HTMLVideoElement|Image} Canvas. + */ +ol.renderer.canvas.Layer.prototype.getImage = goog.abstractMethod; + + +/** + * @return {!goog.vec.Mat4.Number} Image transform. + */ +ol.renderer.canvas.Layer.prototype.getImageTransform = goog.abstractMethod; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {number} offsetX Offset on the x-axis in view coordinates. + * @protected + * @return {!goog.vec.Mat4.Number} Transform. + */ +ol.renderer.canvas.Layer.prototype.getTransform = function(frameState, offsetX) { + var viewState = frameState.viewState; + var pixelRatio = frameState.pixelRatio; + return ol.vec.Mat4.makeTransform2D(this.transform_, + pixelRatio * frameState.size[0] / 2, + pixelRatio * frameState.size[1] / 2, + pixelRatio / viewState.resolution, + -pixelRatio / viewState.resolution, + -viewState.rotation, + -viewState.center[0] + offsetX, + -viewState.center[1]); +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @return {boolean} whether composeFrame should be called. + */ +ol.renderer.canvas.Layer.prototype.prepareFrame = goog.abstractMethod; + + +/** + * @param {ol.Pixel} pixelOnMap Pixel. + * @param {goog.vec.Mat4.Number} imageTransformInv The transformation matrix + * to convert from a map pixel to a canvas pixel. + * @return {ol.Pixel} The pixel. + * @protected + */ +ol.renderer.canvas.Layer.prototype.getPixelOnCanvas = function(pixelOnMap, imageTransformInv) { + var pixelOnCanvas = [0, 0]; + ol.vec.Mat4.multVec2(imageTransformInv, pixelOnMap, pixelOnCanvas); + return pixelOnCanvas; +}; + +goog.provide('ol.render.IReplayGroup'); + +goog.require('ol.render.VectorContext'); + + +/** + * @enum {string} + */ +ol.render.ReplayType = { + IMAGE: 'Image', + LINE_STRING: 'LineString', + POLYGON: 'Polygon', + TEXT: 'Text' +}; + + +/** + * @const + * @type {Array.<ol.render.ReplayType>} + */ +ol.render.REPLAY_ORDER = [ + ol.render.ReplayType.POLYGON, + ol.render.ReplayType.LINE_STRING, + ol.render.ReplayType.IMAGE, + ol.render.ReplayType.TEXT +]; + + +/** + * @interface + */ +ol.render.IReplayGroup = function() { +}; + + +/* eslint-disable valid-jsdoc */ +// TODO: enable valid-jsdoc for @interface methods when this issue is resolved +// https://github.com/eslint/eslint/issues/4887 + + +/** + * @param {number|undefined} zIndex Z index. + * @param {ol.render.ReplayType} replayType Replay type. + * @return {ol.render.VectorContext} Replay. + */ +ol.render.IReplayGroup.prototype.getReplay = function(zIndex, replayType) { +}; + + +/** + * @return {boolean} Is empty. + */ +ol.render.IReplayGroup.prototype.isEmpty = function() { +}; + +// FIXME add option to apply snapToPixel to all coordinates? +// FIXME can eliminate empty set styles and strokes (when all geoms skipped) + +goog.provide('ol.render.canvas.ImageReplay'); +goog.provide('ol.render.canvas.LineStringReplay'); +goog.provide('ol.render.canvas.PolygonReplay'); +goog.provide('ol.render.canvas.Replay'); +goog.provide('ol.render.canvas.ReplayGroup'); +goog.provide('ol.render.canvas.TextReplay'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol'); +goog.require('ol.array'); +goog.require('ol.color'); +goog.require('ol.colorlike'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.extent.Relationship'); +goog.require('ol.geom.flat.simplify'); +goog.require('ol.geom.flat.transform'); +goog.require('ol.has'); +goog.require('ol.object'); +goog.require('ol.render.IReplayGroup'); +goog.require('ol.render.VectorContext'); +goog.require('ol.render.canvas'); +goog.require('ol.vec.Mat4'); + + +/** + * @enum {number} + */ +ol.render.canvas.Instruction = { + BEGIN_GEOMETRY: 0, + BEGIN_PATH: 1, + CIRCLE: 2, + CLOSE_PATH: 3, + DRAW_IMAGE: 4, + DRAW_TEXT: 5, + END_GEOMETRY: 6, + FILL: 7, + MOVE_TO_LINE_TO: 8, + SET_FILL_STYLE: 9, + SET_STROKE_STYLE: 10, + SET_TEXT_STYLE: 11, + STROKE: 12 +}; + + +/** + * @constructor + * @extends {ol.render.VectorContext} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @protected + * @struct + */ +ol.render.canvas.Replay = function(tolerance, maxExtent, resolution) { + ol.render.VectorContext.call(this); + + /** + * @protected + * @type {number} + */ + this.tolerance = tolerance; + + /** + * @protected + * @const + * @type {ol.Extent} + */ + this.maxExtent = maxExtent; + + /** + * @private + * @type {ol.Extent} + */ + this.bufferedMaxExtent_ = null; + + /** + * @protected + * @type {number} + */ + this.maxLineWidth = 0; + + /** + * @protected + * @const + * @type {number} + */ + this.resolution = resolution; + + /** + * @private + * @type {Array.<*>} + */ + this.beginGeometryInstruction1_ = null; + + /** + * @private + * @type {Array.<*>} + */ + this.beginGeometryInstruction2_ = null; + + /** + * @protected + * @type {Array.<*>} + */ + this.instructions = []; + + /** + * @protected + * @type {Array.<number>} + */ + this.coordinates = []; + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.renderedTransform_ = goog.vec.Mat4.createNumber(); + + /** + * @protected + * @type {Array.<*>} + */ + this.hitDetectionInstructions = []; + + /** + * @private + * @type {Array.<number>} + */ + this.pixelCoordinates_ = []; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.tmpLocalTransform_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.tmpLocalTransformInv_ = goog.vec.Mat4.createNumber(); +}; +ol.inherits(ol.render.canvas.Replay, ol.render.VectorContext); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {boolean} close Close. + * @protected + * @return {number} My end. + */ +ol.render.canvas.Replay.prototype.appendFlatCoordinates = function(flatCoordinates, offset, end, stride, close) { + + var myEnd = this.coordinates.length; + var extent = this.getBufferedMaxExtent(); + var lastCoord = [flatCoordinates[offset], flatCoordinates[offset + 1]]; + var nextCoord = [NaN, NaN]; + var skipped = true; + + var i, lastRel, nextRel; + for (i = offset + stride; i < end; i += stride) { + nextCoord[0] = flatCoordinates[i]; + nextCoord[1] = flatCoordinates[i + 1]; + nextRel = ol.extent.coordinateRelationship(extent, nextCoord); + if (nextRel !== lastRel) { + if (skipped) { + this.coordinates[myEnd++] = lastCoord[0]; + this.coordinates[myEnd++] = lastCoord[1]; + } + this.coordinates[myEnd++] = nextCoord[0]; + this.coordinates[myEnd++] = nextCoord[1]; + skipped = false; + } else if (nextRel === ol.extent.Relationship.INTERSECTING) { + this.coordinates[myEnd++] = nextCoord[0]; + this.coordinates[myEnd++] = nextCoord[1]; + skipped = false; + } else { + skipped = true; + } + lastCoord[0] = nextCoord[0]; + lastCoord[1] = nextCoord[1]; + lastRel = nextRel; + } + + // handle case where there is only one point to append + if (i === offset + stride) { + this.coordinates[myEnd++] = lastCoord[0]; + this.coordinates[myEnd++] = lastCoord[1]; + } + + if (close) { + this.coordinates[myEnd++] = flatCoordinates[offset]; + this.coordinates[myEnd++] = flatCoordinates[offset + 1]; + } + return myEnd; +}; + + +/** + * @protected + * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.canvas.Replay.prototype.beginGeometry = function(geometry, feature) { + this.beginGeometryInstruction1_ = + [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0]; + this.instructions.push(this.beginGeometryInstruction1_); + this.beginGeometryInstruction2_ = + [ol.render.canvas.Instruction.BEGIN_GEOMETRY, feature, 0]; + this.hitDetectionInstructions.push(this.beginGeometryInstruction2_); +}; + + +/** + * @private + * @param {CanvasRenderingContext2D} context Context. + * @param {number} pixelRatio Pixel ratio. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {Array.<*>} instructions Instructions array. + * @param {function((ol.Feature|ol.render.Feature)): T|undefined} + * featureCallback Feature callback. + * @param {ol.Extent=} opt_hitExtent Only check features that intersect this + * extent. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.canvas.Replay.prototype.replay_ = function( + context, pixelRatio, transform, viewRotation, skippedFeaturesHash, + instructions, featureCallback, opt_hitExtent) { + /** @type {Array.<number>} */ + var pixelCoordinates; + if (ol.vec.Mat4.equals2D(transform, this.renderedTransform_)) { + pixelCoordinates = this.pixelCoordinates_; + } else { + pixelCoordinates = ol.geom.flat.transform.transform2D( + this.coordinates, 0, this.coordinates.length, 2, + transform, this.pixelCoordinates_); + goog.vec.Mat4.setFromArray(this.renderedTransform_, transform); + goog.asserts.assert(pixelCoordinates === this.pixelCoordinates_, + 'pixelCoordinates should be the same as this.pixelCoordinates_'); + } + var skipFeatures = !ol.object.isEmpty(skippedFeaturesHash); + var i = 0; // instruction index + var ii = instructions.length; // end of instructions + var d = 0; // data index + var dd; // end of per-instruction data + var localTransform = this.tmpLocalTransform_; + var localTransformInv = this.tmpLocalTransformInv_; + var prevX, prevY, roundX, roundY; + while (i < ii) { + var instruction = instructions[i]; + var type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]); + var feature, fill, stroke, text, x, y; + switch (type) { + case ol.render.canvas.Instruction.BEGIN_GEOMETRY: + feature = /** @type {ol.Feature|ol.render.Feature} */ (instruction[1]); + if ((skipFeatures && + skippedFeaturesHash[goog.getUid(feature).toString()]) || + !feature.getGeometry()) { + i = /** @type {number} */ (instruction[2]); + } else if (opt_hitExtent !== undefined && !ol.extent.intersects( + opt_hitExtent, feature.getGeometry().getExtent())) { + i = /** @type {number} */ (instruction[2]); + } else { + ++i; + } + break; + case ol.render.canvas.Instruction.BEGIN_PATH: + context.beginPath(); + ++i; + break; + case ol.render.canvas.Instruction.CIRCLE: + goog.asserts.assert(goog.isNumber(instruction[1]), + 'second instruction should be a number'); + d = /** @type {number} */ (instruction[1]); + var x1 = pixelCoordinates[d]; + var y1 = pixelCoordinates[d + 1]; + var x2 = pixelCoordinates[d + 2]; + var y2 = pixelCoordinates[d + 3]; + var dx = x2 - x1; + var dy = y2 - y1; + var r = Math.sqrt(dx * dx + dy * dy); + context.arc(x1, y1, r, 0, 2 * Math.PI, true); + ++i; + break; + case ol.render.canvas.Instruction.CLOSE_PATH: + context.closePath(); + ++i; + break; + case ol.render.canvas.Instruction.DRAW_IMAGE: + goog.asserts.assert(goog.isNumber(instruction[1]), + 'second instruction should be a number'); + d = /** @type {number} */ (instruction[1]); + goog.asserts.assert(goog.isNumber(instruction[2]), + 'third instruction should be a number'); + dd = /** @type {number} */ (instruction[2]); + var image = /** @type {HTMLCanvasElement|HTMLVideoElement|Image} */ + (instruction[3]); + // Remaining arguments in DRAW_IMAGE are in alphabetical order + var anchorX = /** @type {number} */ (instruction[4]) * pixelRatio; + var anchorY = /** @type {number} */ (instruction[5]) * pixelRatio; + var height = /** @type {number} */ (instruction[6]); + var opacity = /** @type {number} */ (instruction[7]); + var originX = /** @type {number} */ (instruction[8]); + var originY = /** @type {number} */ (instruction[9]); + var rotateWithView = /** @type {boolean} */ (instruction[10]); + var rotation = /** @type {number} */ (instruction[11]); + var scale = /** @type {number} */ (instruction[12]); + var snapToPixel = /** @type {boolean} */ (instruction[13]); + var width = /** @type {number} */ (instruction[14]); + if (rotateWithView) { + rotation += viewRotation; + } + for (; d < dd; d += 2) { + x = pixelCoordinates[d] - anchorX; + y = pixelCoordinates[d + 1] - anchorY; + if (snapToPixel) { + x = Math.round(x); + y = Math.round(y); + } + if (scale != 1 || rotation !== 0) { + var centerX = x + anchorX; + var centerY = y + anchorY; + ol.vec.Mat4.makeTransform2D( + localTransform, centerX, centerY, scale, scale, + rotation, -centerX, -centerY); + context.transform( + goog.vec.Mat4.getElement(localTransform, 0, 0), + goog.vec.Mat4.getElement(localTransform, 1, 0), + goog.vec.Mat4.getElement(localTransform, 0, 1), + goog.vec.Mat4.getElement(localTransform, 1, 1), + goog.vec.Mat4.getElement(localTransform, 0, 3), + goog.vec.Mat4.getElement(localTransform, 1, 3)); + } + var alpha = context.globalAlpha; + if (opacity != 1) { + context.globalAlpha = alpha * opacity; + } + + var w = (width + originX > image.width) ? image.width - originX : width; + var h = (height + originY > image.height) ? image.height - originY : height; + + context.drawImage(image, originX, originY, w, h, + x, y, w * pixelRatio, h * pixelRatio); + + if (opacity != 1) { + context.globalAlpha = alpha; + } + if (scale != 1 || rotation !== 0) { + goog.vec.Mat4.invert(localTransform, localTransformInv); + context.transform( + goog.vec.Mat4.getElement(localTransformInv, 0, 0), + goog.vec.Mat4.getElement(localTransformInv, 1, 0), + goog.vec.Mat4.getElement(localTransformInv, 0, 1), + goog.vec.Mat4.getElement(localTransformInv, 1, 1), + goog.vec.Mat4.getElement(localTransformInv, 0, 3), + goog.vec.Mat4.getElement(localTransformInv, 1, 3)); + } + } + ++i; + break; + case ol.render.canvas.Instruction.DRAW_TEXT: + goog.asserts.assert(goog.isNumber(instruction[1]), + '2nd instruction should be a number'); + d = /** @type {number} */ (instruction[1]); + goog.asserts.assert(goog.isNumber(instruction[2]), + '3rd instruction should be a number'); + dd = /** @type {number} */ (instruction[2]); + goog.asserts.assert(typeof instruction[3] === 'string', + '4th instruction should be a string'); + text = /** @type {string} */ (instruction[3]); + goog.asserts.assert(goog.isNumber(instruction[4]), + '5th instruction should be a number'); + var offsetX = /** @type {number} */ (instruction[4]) * pixelRatio; + goog.asserts.assert(goog.isNumber(instruction[5]), + '6th instruction should be a number'); + var offsetY = /** @type {number} */ (instruction[5]) * pixelRatio; + goog.asserts.assert(goog.isNumber(instruction[6]), + '7th instruction should be a number'); + rotation = /** @type {number} */ (instruction[6]); + goog.asserts.assert(goog.isNumber(instruction[7]), + '8th instruction should be a number'); + scale = /** @type {number} */ (instruction[7]) * pixelRatio; + goog.asserts.assert(typeof instruction[8] === 'boolean', + '9th instruction should be a boolean'); + fill = /** @type {boolean} */ (instruction[8]); + goog.asserts.assert(typeof instruction[9] === 'boolean', + '10th instruction should be a boolean'); + stroke = /** @type {boolean} */ (instruction[9]); + for (; d < dd; d += 2) { + x = pixelCoordinates[d] + offsetX; + y = pixelCoordinates[d + 1] + offsetY; + if (scale != 1 || rotation !== 0) { + ol.vec.Mat4.makeTransform2D( + localTransform, x, y, scale, scale, rotation, -x, -y); + context.transform( + goog.vec.Mat4.getElement(localTransform, 0, 0), + goog.vec.Mat4.getElement(localTransform, 1, 0), + goog.vec.Mat4.getElement(localTransform, 0, 1), + goog.vec.Mat4.getElement(localTransform, 1, 1), + goog.vec.Mat4.getElement(localTransform, 0, 3), + goog.vec.Mat4.getElement(localTransform, 1, 3)); + } + + // Support multiple lines separated by \n + var lines = text.split('\n'); + var numLines = lines.length; + var fontSize, lineY; + if (numLines > 1) { + // Estimate line height using width of capital M, and add padding + fontSize = Math.round(context.measureText('M').width * 1.5); + lineY = y - (((numLines - 1) / 2) * fontSize); + } else { + // No need to calculate line height/offset for a single line + fontSize = 0; + lineY = y; + } + + for (var lineIndex = 0; lineIndex < numLines; lineIndex++) { + var line = lines[lineIndex]; + if (stroke) { + context.strokeText(line, x, lineY); + } + if (fill) { + context.fillText(line, x, lineY); + } + + // Move next line down by fontSize px + lineY = lineY + fontSize; + } + + if (scale != 1 || rotation !== 0) { + goog.vec.Mat4.invert(localTransform, localTransformInv); + context.transform( + goog.vec.Mat4.getElement(localTransformInv, 0, 0), + goog.vec.Mat4.getElement(localTransformInv, 1, 0), + goog.vec.Mat4.getElement(localTransformInv, 0, 1), + goog.vec.Mat4.getElement(localTransformInv, 1, 1), + goog.vec.Mat4.getElement(localTransformInv, 0, 3), + goog.vec.Mat4.getElement(localTransformInv, 1, 3)); + } + } + ++i; + break; + case ol.render.canvas.Instruction.END_GEOMETRY: + if (featureCallback !== undefined) { + feature = + /** @type {ol.Feature|ol.render.Feature} */ (instruction[1]); + var result = featureCallback(feature); + if (result) { + return result; + } + } + ++i; + break; + case ol.render.canvas.Instruction.FILL: + context.fill(); + ++i; + break; + case ol.render.canvas.Instruction.MOVE_TO_LINE_TO: + goog.asserts.assert(goog.isNumber(instruction[1]), + '2nd instruction should be a number'); + d = /** @type {number} */ (instruction[1]); + goog.asserts.assert(goog.isNumber(instruction[2]), + '3rd instruction should be a number'); + dd = /** @type {number} */ (instruction[2]); + x = pixelCoordinates[d]; + y = pixelCoordinates[d + 1]; + roundX = (x + 0.5) | 0; + roundY = (y + 0.5) | 0; + if (roundX !== prevX || roundY !== prevY) { + context.moveTo(x, y); + prevX = roundX; + prevY = roundY; + } + for (d += 2; d < dd; d += 2) { + x = pixelCoordinates[d]; + y = pixelCoordinates[d + 1]; + roundX = (x + 0.5) | 0; + roundY = (y + 0.5) | 0; + if (d == dd - 2 || roundX !== prevX || roundY !== prevY) { + context.lineTo(x, y); + prevX = roundX; + prevY = roundY; + } + } + ++i; + break; + case ol.render.canvas.Instruction.SET_FILL_STYLE: + goog.asserts.assert( + ol.colorlike.isColorLike(instruction[1]), + '2nd instruction should be a string, ' + + 'CanvasPattern, or CanvasGradient'); + context.fillStyle = /** @type {ol.ColorLike} */ (instruction[1]); + ++i; + break; + case ol.render.canvas.Instruction.SET_STROKE_STYLE: + goog.asserts.assert(typeof instruction[1] === 'string', + '2nd instruction should be a string'); + goog.asserts.assert(goog.isNumber(instruction[2]), + '3rd instruction should be a number'); + goog.asserts.assert(typeof instruction[3] === 'string', + '4rd instruction should be a string'); + goog.asserts.assert(typeof instruction[4] === 'string', + '5th instruction should be a string'); + goog.asserts.assert(goog.isNumber(instruction[5]), + '6th instruction should be a number'); + goog.asserts.assert(instruction[6], + '7th instruction should not be null'); + var usePixelRatio = instruction[7] !== undefined ? + instruction[7] : true; + var lineWidth = /** @type {number} */ (instruction[2]); + context.strokeStyle = /** @type {string} */ (instruction[1]); + context.lineWidth = usePixelRatio ? lineWidth * pixelRatio : lineWidth; + context.lineCap = /** @type {string} */ (instruction[3]); + context.lineJoin = /** @type {string} */ (instruction[4]); + context.miterLimit = /** @type {number} */ (instruction[5]); + if (ol.has.CANVAS_LINE_DASH) { + context.setLineDash(/** @type {Array.<number>} */ (instruction[6])); + } + prevX = NaN; + prevY = NaN; + ++i; + break; + case ol.render.canvas.Instruction.SET_TEXT_STYLE: + goog.asserts.assert(typeof instruction[1] === 'string', + '2nd instruction should be a string'); + goog.asserts.assert(typeof instruction[2] === 'string', + '3rd instruction should be a string'); + goog.asserts.assert(typeof instruction[3] === 'string', + '4th instruction should be a string'); + context.font = /** @type {string} */ (instruction[1]); + context.textAlign = /** @type {string} */ (instruction[2]); + context.textBaseline = /** @type {string} */ (instruction[3]); + ++i; + break; + case ol.render.canvas.Instruction.STROKE: + context.stroke(); + ++i; + break; + default: + goog.asserts.fail('Unknown canvas render instruction'); + ++i; // consume the instruction anyway, to avoid an infinite loop + break; + } + } + // assert that all instructions were consumed + goog.asserts.assert(i == instructions.length, + 'all instructions should be consumed'); + return undefined; +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {number} pixelRatio Pixel ratio. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + */ +ol.render.canvas.Replay.prototype.replay = function( + context, pixelRatio, transform, viewRotation, skippedFeaturesHash) { + var instructions = this.instructions; + this.replay_(context, pixelRatio, transform, viewRotation, + skippedFeaturesHash, instructions, undefined); +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T=} opt_featureCallback + * Feature callback. + * @param {ol.Extent=} opt_hitExtent Only check features that intersect this + * extent. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.canvas.Replay.prototype.replayHitDetection = function( + context, transform, viewRotation, skippedFeaturesHash, + opt_featureCallback, opt_hitExtent) { + var instructions = this.hitDetectionInstructions; + return this.replay_(context, 1, transform, viewRotation, + skippedFeaturesHash, instructions, opt_featureCallback, opt_hitExtent); +}; + + +/** + * @private + */ +ol.render.canvas.Replay.prototype.reverseHitDetectionInstructions_ = function() { + var hitDetectionInstructions = this.hitDetectionInstructions; + // step 1 - reverse array + hitDetectionInstructions.reverse(); + // step 2 - reverse instructions within geometry blocks + var i; + var n = hitDetectionInstructions.length; + var instruction; + var type; + var begin = -1; + for (i = 0; i < n; ++i) { + instruction = hitDetectionInstructions[i]; + type = /** @type {ol.render.canvas.Instruction} */ (instruction[0]); + if (type == ol.render.canvas.Instruction.END_GEOMETRY) { + goog.asserts.assert(begin == -1, 'begin should be -1'); + begin = i; + } else if (type == ol.render.canvas.Instruction.BEGIN_GEOMETRY) { + instruction[2] = i; + goog.asserts.assert(begin >= 0, + 'begin should be larger than or equal to 0'); + ol.array.reverseSubArray(this.hitDetectionInstructions, begin, i); + begin = -1; + } + } +}; + + +/** + * @param {ol.geom.Geometry|ol.render.Feature} geometry Geometry. + * @param {ol.Feature|ol.render.Feature} feature Feature. + */ +ol.render.canvas.Replay.prototype.endGeometry = function(geometry, feature) { + goog.asserts.assert(this.beginGeometryInstruction1_, + 'this.beginGeometryInstruction1_ should not be null'); + this.beginGeometryInstruction1_[2] = this.instructions.length; + this.beginGeometryInstruction1_ = null; + goog.asserts.assert(this.beginGeometryInstruction2_, + 'this.beginGeometryInstruction2_ should not be null'); + this.beginGeometryInstruction2_[2] = this.hitDetectionInstructions.length; + this.beginGeometryInstruction2_ = null; + var endGeometryInstruction = + [ol.render.canvas.Instruction.END_GEOMETRY, feature]; + this.instructions.push(endGeometryInstruction); + this.hitDetectionInstructions.push(endGeometryInstruction); +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.render.canvas.Replay.prototype.finish = ol.nullFunction; + + +/** + * Get the buffered rendering extent. Rendering will be clipped to the extent + * provided to the constructor. To account for symbolizers that may intersect + * this extent, we calculate a buffered extent (e.g. based on stroke width). + * @return {ol.Extent} The buffered rendering extent. + * @protected + */ +ol.render.canvas.Replay.prototype.getBufferedMaxExtent = function() { + return this.maxExtent; +}; + + +/** + * @constructor + * @extends {ol.render.canvas.Replay} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @protected + * @struct + */ +ol.render.canvas.ImageReplay = function(tolerance, maxExtent, resolution) { + ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution); + + /** + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|Image} + */ + this.hitDetectionImage_ = null; + + /** + * @private + * @type {HTMLCanvasElement|HTMLVideoElement|Image} + */ + this.image_ = null; + + /** + * @private + * @type {number|undefined} + */ + this.anchorX_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.anchorY_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.height_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.opacity_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.originX_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.originY_ = undefined; + + /** + * @private + * @type {boolean|undefined} + */ + this.rotateWithView_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.scale_ = undefined; + + /** + * @private + * @type {boolean|undefined} + */ + this.snapToPixel_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.width_ = undefined; + +}; +ol.inherits(ol.render.canvas.ImageReplay, ol.render.canvas.Replay); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @private + * @return {number} My end. + */ +ol.render.canvas.ImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) { + return this.appendFlatCoordinates( + flatCoordinates, offset, end, stride, false); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.ImageReplay.prototype.drawPoint = function(pointGeometry, feature) { + if (!this.image_) { + return; + } + goog.asserts.assert(this.anchorX_ !== undefined, + 'this.anchorX_ should be defined'); + goog.asserts.assert(this.anchorY_ !== undefined, + 'this.anchorY_ should be defined'); + goog.asserts.assert(this.height_ !== undefined, + 'this.height_ should be defined'); + goog.asserts.assert(this.opacity_ !== undefined, + 'this.opacity_ should be defined'); + goog.asserts.assert(this.originX_ !== undefined, + 'this.originX_ should be defined'); + goog.asserts.assert(this.originY_ !== undefined, + 'this.originY_ should be defined'); + goog.asserts.assert(this.rotateWithView_ !== undefined, + 'this.rotateWithView_ should be defined'); + goog.asserts.assert(this.rotation_ !== undefined, + 'this.rotation_ should be defined'); + goog.asserts.assert(this.scale_ !== undefined, + 'this.scale_ should be defined'); + goog.asserts.assert(this.width_ !== undefined, + 'this.width_ should be defined'); + this.beginGeometry(pointGeometry, feature); + var flatCoordinates = pointGeometry.getFlatCoordinates(); + var stride = pointGeometry.getStride(); + var myBegin = this.coordinates.length; + var myEnd = this.drawCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); + this.instructions.push([ + ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.hitDetectionInstructions.push([ + ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, + this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.endGeometry(pointGeometry, feature); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.ImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) { + if (!this.image_) { + return; + } + goog.asserts.assert(this.anchorX_ !== undefined, + 'this.anchorX_ should be defined'); + goog.asserts.assert(this.anchorY_ !== undefined, + 'this.anchorY_ should be defined'); + goog.asserts.assert(this.height_ !== undefined, + 'this.height_ should be defined'); + goog.asserts.assert(this.opacity_ !== undefined, + 'this.opacity_ should be defined'); + goog.asserts.assert(this.originX_ !== undefined, + 'this.originX_ should be defined'); + goog.asserts.assert(this.originY_ !== undefined, + 'this.originY_ should be defined'); + goog.asserts.assert(this.rotateWithView_ !== undefined, + 'this.rotateWithView_ should be defined'); + goog.asserts.assert(this.rotation_ !== undefined, + 'this.rotation_ should be defined'); + goog.asserts.assert(this.scale_ !== undefined, + 'this.scale_ should be defined'); + goog.asserts.assert(this.width_ !== undefined, + 'this.width_ should be defined'); + this.beginGeometry(multiPointGeometry, feature); + var flatCoordinates = multiPointGeometry.getFlatCoordinates(); + var stride = multiPointGeometry.getStride(); + var myBegin = this.coordinates.length; + var myEnd = this.drawCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); + this.instructions.push([ + ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, this.image_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.hitDetectionInstructions.push([ + ol.render.canvas.Instruction.DRAW_IMAGE, myBegin, myEnd, + this.hitDetectionImage_, + // Remaining arguments to DRAW_IMAGE are in alphabetical order + this.anchorX_, this.anchorY_, this.height_, this.opacity_, + this.originX_, this.originY_, this.rotateWithView_, this.rotation_, + this.scale_, this.snapToPixel_, this.width_ + ]); + this.endGeometry(multiPointGeometry, feature); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.ImageReplay.prototype.finish = function() { + this.reverseHitDetectionInstructions_(); + // FIXME this doesn't really protect us against further calls to draw*Geometry + this.anchorX_ = undefined; + this.anchorY_ = undefined; + this.hitDetectionImage_ = null; + this.image_ = null; + this.height_ = undefined; + this.scale_ = undefined; + this.opacity_ = undefined; + this.originX_ = undefined; + this.originY_ = undefined; + this.rotateWithView_ = undefined; + this.rotation_ = undefined; + this.snapToPixel_ = undefined; + this.width_ = undefined; +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.ImageReplay.prototype.setImageStyle = function(imageStyle) { + goog.asserts.assert(imageStyle, 'imageStyle should not be null'); + var anchor = imageStyle.getAnchor(); + goog.asserts.assert(anchor, 'anchor should not be null'); + var size = imageStyle.getSize(); + goog.asserts.assert(size, 'size should not be null'); + var hitDetectionImage = imageStyle.getHitDetectionImage(1); + goog.asserts.assert(hitDetectionImage, + 'hitDetectionImage should not be null'); + var image = imageStyle.getImage(1); + goog.asserts.assert(image, 'image should not be null'); + var origin = imageStyle.getOrigin(); + goog.asserts.assert(origin, 'origin should not be null'); + this.anchorX_ = anchor[0]; + this.anchorY_ = anchor[1]; + this.hitDetectionImage_ = hitDetectionImage; + this.image_ = image; + this.height_ = size[1]; + this.opacity_ = imageStyle.getOpacity(); + this.originX_ = origin[0]; + this.originY_ = origin[1]; + this.rotateWithView_ = imageStyle.getRotateWithView(); + this.rotation_ = imageStyle.getRotation(); + this.scale_ = imageStyle.getScale(); + this.snapToPixel_ = imageStyle.getSnapToPixel(); + this.width_ = size[0]; +}; + + +/** + * @constructor + * @extends {ol.render.canvas.Replay} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @protected + * @struct + */ +ol.render.canvas.LineStringReplay = function(tolerance, maxExtent, resolution) { + + ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution); + + /** + * @private + * @type {{currentStrokeStyle: (string|undefined), + * currentLineCap: (string|undefined), + * currentLineDash: Array.<number>, + * currentLineJoin: (string|undefined), + * currentLineWidth: (number|undefined), + * currentMiterLimit: (number|undefined), + * lastStroke: number, + * strokeStyle: (string|undefined), + * lineCap: (string|undefined), + * lineDash: Array.<number>, + * lineJoin: (string|undefined), + * lineWidth: (number|undefined), + * miterLimit: (number|undefined)}|null} + */ + this.state_ = { + currentStrokeStyle: undefined, + currentLineCap: undefined, + currentLineDash: null, + currentLineJoin: undefined, + currentLineWidth: undefined, + currentMiterLimit: undefined, + lastStroke: 0, + strokeStyle: undefined, + lineCap: undefined, + lineDash: null, + lineJoin: undefined, + lineWidth: undefined, + miterLimit: undefined + }; + +}; +ol.inherits(ol.render.canvas.LineStringReplay, ol.render.canvas.Replay); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @private + * @return {number} end. + */ +ol.render.canvas.LineStringReplay.prototype.drawFlatCoordinates_ = function(flatCoordinates, offset, end, stride) { + var myBegin = this.coordinates.length; + var myEnd = this.appendFlatCoordinates( + flatCoordinates, offset, end, stride, false); + var moveToLineToInstruction = + [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd]; + this.instructions.push(moveToLineToInstruction); + this.hitDetectionInstructions.push(moveToLineToInstruction); + return end; +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.LineStringReplay.prototype.getBufferedMaxExtent = function() { + if (!this.bufferedMaxExtent_) { + this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent); + if (this.maxLineWidth > 0) { + var width = this.resolution * (this.maxLineWidth + 1) / 2; + ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_); + } + } + return this.bufferedMaxExtent_; +}; + + +/** + * @private + */ +ol.render.canvas.LineStringReplay.prototype.setStrokeStyle_ = function() { + var state = this.state_; + var strokeStyle = state.strokeStyle; + var lineCap = state.lineCap; + var lineDash = state.lineDash; + var lineJoin = state.lineJoin; + var lineWidth = state.lineWidth; + var miterLimit = state.miterLimit; + goog.asserts.assert(strokeStyle !== undefined, + 'strokeStyle should be defined'); + goog.asserts.assert(lineCap !== undefined, 'lineCap should be defined'); + goog.asserts.assert(lineDash, 'lineDash should not be null'); + goog.asserts.assert(lineJoin !== undefined, 'lineJoin should be defined'); + goog.asserts.assert(lineWidth !== undefined, 'lineWidth should be defined'); + goog.asserts.assert(miterLimit !== undefined, 'miterLimit should be defined'); + if (state.currentStrokeStyle != strokeStyle || + state.currentLineCap != lineCap || + !ol.array.equals(state.currentLineDash, lineDash) || + state.currentLineJoin != lineJoin || + state.currentLineWidth != lineWidth || + state.currentMiterLimit != miterLimit) { + if (state.lastStroke != this.coordinates.length) { + this.instructions.push( + [ol.render.canvas.Instruction.STROKE]); + state.lastStroke = this.coordinates.length; + } + this.instructions.push( + [ol.render.canvas.Instruction.SET_STROKE_STYLE, + strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash], + [ol.render.canvas.Instruction.BEGIN_PATH]); + state.currentStrokeStyle = strokeStyle; + state.currentLineCap = lineCap; + state.currentLineDash = lineDash; + state.currentLineJoin = lineJoin; + state.currentLineWidth = lineWidth; + state.currentMiterLimit = miterLimit; + } +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.LineStringReplay.prototype.drawLineString = function(lineStringGeometry, feature) { + var state = this.state_; + goog.asserts.assert(state, 'state should not be null'); + var strokeStyle = state.strokeStyle; + var lineWidth = state.lineWidth; + if (strokeStyle === undefined || lineWidth === undefined) { + return; + } + this.setStrokeStyle_(); + this.beginGeometry(lineStringGeometry, feature); + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash], + [ol.render.canvas.Instruction.BEGIN_PATH]); + var flatCoordinates = lineStringGeometry.getFlatCoordinates(); + var stride = lineStringGeometry.getStride(); + this.drawFlatCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); + this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]); + this.endGeometry(lineStringGeometry, feature); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.LineStringReplay.prototype.drawMultiLineString = function(multiLineStringGeometry, feature) { + var state = this.state_; + goog.asserts.assert(state, 'state should not be null'); + var strokeStyle = state.strokeStyle; + var lineWidth = state.lineWidth; + if (strokeStyle === undefined || lineWidth === undefined) { + return; + } + this.setStrokeStyle_(); + this.beginGeometry(multiLineStringGeometry, feature); + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash], + [ol.render.canvas.Instruction.BEGIN_PATH]); + var ends = multiLineStringGeometry.getEnds(); + var flatCoordinates = multiLineStringGeometry.getFlatCoordinates(); + var stride = multiLineStringGeometry.getStride(); + var offset = 0; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + offset = this.drawFlatCoordinates_( + flatCoordinates, offset, ends[i], stride); + } + this.hitDetectionInstructions.push([ol.render.canvas.Instruction.STROKE]); + this.endGeometry(multiLineStringGeometry, feature); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.LineStringReplay.prototype.finish = function() { + var state = this.state_; + goog.asserts.assert(state, 'state should not be null'); + if (state.lastStroke != this.coordinates.length) { + this.instructions.push([ol.render.canvas.Instruction.STROKE]); + } + this.reverseHitDetectionInstructions_(); + this.state_ = null; +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.LineStringReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { + goog.asserts.assert(this.state_, 'this.state_ should not be null'); + goog.asserts.assert(!fillStyle, 'fillStyle should be null'); + goog.asserts.assert(strokeStyle, 'strokeStyle should not be null'); + var strokeStyleColor = strokeStyle.getColor(); + this.state_.strokeStyle = ol.color.asString(strokeStyleColor ? + strokeStyleColor : ol.render.canvas.defaultStrokeStyle); + var strokeStyleLineCap = strokeStyle.getLineCap(); + this.state_.lineCap = strokeStyleLineCap !== undefined ? + strokeStyleLineCap : ol.render.canvas.defaultLineCap; + var strokeStyleLineDash = strokeStyle.getLineDash(); + this.state_.lineDash = strokeStyleLineDash ? + strokeStyleLineDash : ol.render.canvas.defaultLineDash; + var strokeStyleLineJoin = strokeStyle.getLineJoin(); + this.state_.lineJoin = strokeStyleLineJoin !== undefined ? + strokeStyleLineJoin : ol.render.canvas.defaultLineJoin; + var strokeStyleWidth = strokeStyle.getWidth(); + this.state_.lineWidth = strokeStyleWidth !== undefined ? + strokeStyleWidth : ol.render.canvas.defaultLineWidth; + var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); + this.state_.miterLimit = strokeStyleMiterLimit !== undefined ? + strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; + + if (this.state_.lineWidth > this.maxLineWidth) { + this.maxLineWidth = this.state_.lineWidth; + // invalidate the buffered max extent cache + this.bufferedMaxExtent_ = null; + } +}; + + +/** + * @constructor + * @extends {ol.render.canvas.Replay} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @protected + * @struct + */ +ol.render.canvas.PolygonReplay = function(tolerance, maxExtent, resolution) { + + ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution); + + /** + * @private + * @type {{currentFillStyle: (ol.ColorLike|undefined), + * currentStrokeStyle: (string|undefined), + * currentLineCap: (string|undefined), + * currentLineDash: Array.<number>, + * currentLineJoin: (string|undefined), + * currentLineWidth: (number|undefined), + * currentMiterLimit: (number|undefined), + * fillStyle: (ol.ColorLike|undefined), + * strokeStyle: (string|undefined), + * lineCap: (string|undefined), + * lineDash: Array.<number>, + * lineJoin: (string|undefined), + * lineWidth: (number|undefined), + * miterLimit: (number|undefined)}|null} + */ + this.state_ = { + currentFillStyle: undefined, + currentStrokeStyle: undefined, + currentLineCap: undefined, + currentLineDash: null, + currentLineJoin: undefined, + currentLineWidth: undefined, + currentMiterLimit: undefined, + fillStyle: undefined, + strokeStyle: undefined, + lineCap: undefined, + lineDash: null, + lineJoin: undefined, + lineWidth: undefined, + miterLimit: undefined + }; + +}; +ol.inherits(ol.render.canvas.PolygonReplay, ol.render.canvas.Replay); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @private + * @return {number} End. + */ +ol.render.canvas.PolygonReplay.prototype.drawFlatCoordinatess_ = function(flatCoordinates, offset, ends, stride) { + var state = this.state_; + var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH]; + this.instructions.push(beginPathInstruction); + this.hitDetectionInstructions.push(beginPathInstruction); + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + var myBegin = this.coordinates.length; + var myEnd = this.appendFlatCoordinates( + flatCoordinates, offset, end, stride, true); + var moveToLineToInstruction = + [ol.render.canvas.Instruction.MOVE_TO_LINE_TO, myBegin, myEnd]; + var closePathInstruction = [ol.render.canvas.Instruction.CLOSE_PATH]; + this.instructions.push(moveToLineToInstruction, closePathInstruction); + this.hitDetectionInstructions.push(moveToLineToInstruction, + closePathInstruction); + offset = end; + } + // FIXME is it quicker to fill and stroke each polygon individually, + // FIXME or all polygons together? + var fillInstruction = [ol.render.canvas.Instruction.FILL]; + this.hitDetectionInstructions.push(fillInstruction); + if (state.fillStyle !== undefined) { + this.instructions.push(fillInstruction); + } + if (state.strokeStyle !== undefined) { + goog.asserts.assert(state.lineWidth !== undefined, + 'state.lineWidth should be defined'); + var strokeInstruction = [ol.render.canvas.Instruction.STROKE]; + this.instructions.push(strokeInstruction); + this.hitDetectionInstructions.push(strokeInstruction); + } + return offset; +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.PolygonReplay.prototype.drawCircle = function(circleGeometry, feature) { + var state = this.state_; + goog.asserts.assert(state, 'state should not be null'); + var fillStyle = state.fillStyle; + var strokeStyle = state.strokeStyle; + if (fillStyle === undefined && strokeStyle === undefined) { + return; + } + if (strokeStyle !== undefined) { + goog.asserts.assert(state.lineWidth !== undefined, + 'state.lineWidth should be defined'); + } + this.setFillStrokeStyles_(); + this.beginGeometry(circleGeometry, feature); + // always fill the circle for hit detection + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_FILL_STYLE, + ol.color.asString(ol.render.canvas.defaultFillStyle)]); + if (state.strokeStyle !== undefined) { + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash]); + } + var flatCoordinates = circleGeometry.getFlatCoordinates(); + var stride = circleGeometry.getStride(); + var myBegin = this.coordinates.length; + this.appendFlatCoordinates( + flatCoordinates, 0, flatCoordinates.length, stride, false); + var beginPathInstruction = [ol.render.canvas.Instruction.BEGIN_PATH]; + var circleInstruction = [ol.render.canvas.Instruction.CIRCLE, myBegin]; + this.instructions.push(beginPathInstruction, circleInstruction); + this.hitDetectionInstructions.push(beginPathInstruction, circleInstruction); + var fillInstruction = [ol.render.canvas.Instruction.FILL]; + this.hitDetectionInstructions.push(fillInstruction); + if (state.fillStyle !== undefined) { + this.instructions.push(fillInstruction); + } + if (state.strokeStyle !== undefined) { + goog.asserts.assert(state.lineWidth !== undefined, + 'state.lineWidth should be defined'); + var strokeInstruction = [ol.render.canvas.Instruction.STROKE]; + this.instructions.push(strokeInstruction); + this.hitDetectionInstructions.push(strokeInstruction); + } + this.endGeometry(circleGeometry, feature); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.PolygonReplay.prototype.drawPolygon = function(polygonGeometry, feature) { + var state = this.state_; + goog.asserts.assert(state, 'state should not be null'); + var fillStyle = state.fillStyle; + var strokeStyle = state.strokeStyle; + if (fillStyle === undefined && strokeStyle === undefined) { + return; + } + if (strokeStyle !== undefined) { + goog.asserts.assert(state.lineWidth !== undefined, + 'state.lineWidth should be defined'); + } + this.setFillStrokeStyles_(); + this.beginGeometry(polygonGeometry, feature); + // always fill the polygon for hit detection + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_FILL_STYLE, + ol.color.asString(ol.render.canvas.defaultFillStyle)]); + if (state.strokeStyle !== undefined) { + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash]); + } + var ends = polygonGeometry.getEnds(); + var flatCoordinates = polygonGeometry.getOrientedFlatCoordinates(); + var stride = polygonGeometry.getStride(); + this.drawFlatCoordinatess_(flatCoordinates, 0, ends, stride); + this.endGeometry(polygonGeometry, feature); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.PolygonReplay.prototype.drawMultiPolygon = function(multiPolygonGeometry, feature) { + var state = this.state_; + goog.asserts.assert(state, 'state should not be null'); + var fillStyle = state.fillStyle; + var strokeStyle = state.strokeStyle; + if (fillStyle === undefined && strokeStyle === undefined) { + return; + } + if (strokeStyle !== undefined) { + goog.asserts.assert(state.lineWidth !== undefined, + 'state.lineWidth should be defined'); + } + this.setFillStrokeStyles_(); + this.beginGeometry(multiPolygonGeometry, feature); + // always fill the multi-polygon for hit detection + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_FILL_STYLE, + ol.color.asString(ol.render.canvas.defaultFillStyle)]); + if (state.strokeStyle !== undefined) { + this.hitDetectionInstructions.push( + [ol.render.canvas.Instruction.SET_STROKE_STYLE, + state.strokeStyle, state.lineWidth, state.lineCap, state.lineJoin, + state.miterLimit, state.lineDash]); + } + var endss = multiPolygonGeometry.getEndss(); + var flatCoordinates = multiPolygonGeometry.getOrientedFlatCoordinates(); + var stride = multiPolygonGeometry.getStride(); + var offset = 0; + var i, ii; + for (i = 0, ii = endss.length; i < ii; ++i) { + offset = this.drawFlatCoordinatess_( + flatCoordinates, offset, endss[i], stride); + } + this.endGeometry(multiPolygonGeometry, feature); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.PolygonReplay.prototype.finish = function() { + goog.asserts.assert(this.state_, 'this.state_ should not be null'); + this.reverseHitDetectionInstructions_(); + this.state_ = null; + // We want to preserve topology when drawing polygons. Polygons are + // simplified using quantization and point elimination. However, we might + // have received a mix of quantized and non-quantized geometries, so ensure + // that all are quantized by quantizing all coordinates in the batch. + var tolerance = this.tolerance; + if (tolerance !== 0) { + var coordinates = this.coordinates; + var i, ii; + for (i = 0, ii = coordinates.length; i < ii; ++i) { + coordinates[i] = ol.geom.flat.simplify.snap(coordinates[i], tolerance); + } + } +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.PolygonReplay.prototype.getBufferedMaxExtent = function() { + if (!this.bufferedMaxExtent_) { + this.bufferedMaxExtent_ = ol.extent.clone(this.maxExtent); + if (this.maxLineWidth > 0) { + var width = this.resolution * (this.maxLineWidth + 1) / 2; + ol.extent.buffer(this.bufferedMaxExtent_, width, this.bufferedMaxExtent_); + } + } + return this.bufferedMaxExtent_; +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyle = function(fillStyle, strokeStyle) { + goog.asserts.assert(this.state_, 'this.state_ should not be null'); + goog.asserts.assert(fillStyle || strokeStyle, + 'fillStyle or strokeStyle should not be null'); + var state = this.state_; + if (fillStyle) { + var fillStyleColor = fillStyle.getColor(); + state.fillStyle = ol.colorlike.asColorLike(fillStyleColor ? + fillStyleColor : ol.render.canvas.defaultFillStyle); + } else { + state.fillStyle = undefined; + } + if (strokeStyle) { + var strokeStyleColor = strokeStyle.getColor(); + state.strokeStyle = ol.color.asString(strokeStyleColor ? + strokeStyleColor : ol.render.canvas.defaultStrokeStyle); + var strokeStyleLineCap = strokeStyle.getLineCap(); + state.lineCap = strokeStyleLineCap !== undefined ? + strokeStyleLineCap : ol.render.canvas.defaultLineCap; + var strokeStyleLineDash = strokeStyle.getLineDash(); + state.lineDash = strokeStyleLineDash ? + strokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash; + var strokeStyleLineJoin = strokeStyle.getLineJoin(); + state.lineJoin = strokeStyleLineJoin !== undefined ? + strokeStyleLineJoin : ol.render.canvas.defaultLineJoin; + var strokeStyleWidth = strokeStyle.getWidth(); + state.lineWidth = strokeStyleWidth !== undefined ? + strokeStyleWidth : ol.render.canvas.defaultLineWidth; + var strokeStyleMiterLimit = strokeStyle.getMiterLimit(); + state.miterLimit = strokeStyleMiterLimit !== undefined ? + strokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; + + if (state.lineWidth > this.maxLineWidth) { + this.maxLineWidth = state.lineWidth; + // invalidate the buffered max extent cache + this.bufferedMaxExtent_ = null; + } + } else { + state.strokeStyle = undefined; + state.lineCap = undefined; + state.lineDash = null; + state.lineJoin = undefined; + state.lineWidth = undefined; + state.miterLimit = undefined; + } +}; + + +/** + * @private + */ +ol.render.canvas.PolygonReplay.prototype.setFillStrokeStyles_ = function() { + var state = this.state_; + var fillStyle = state.fillStyle; + var strokeStyle = state.strokeStyle; + var lineCap = state.lineCap; + var lineDash = state.lineDash; + var lineJoin = state.lineJoin; + var lineWidth = state.lineWidth; + var miterLimit = state.miterLimit; + if (fillStyle !== undefined && state.currentFillStyle != fillStyle) { + this.instructions.push( + [ol.render.canvas.Instruction.SET_FILL_STYLE, fillStyle]); + state.currentFillStyle = state.fillStyle; + } + if (strokeStyle !== undefined) { + goog.asserts.assert(lineCap !== undefined, 'lineCap should be defined'); + goog.asserts.assert(lineDash, 'lineDash should not be null'); + goog.asserts.assert(lineJoin !== undefined, 'lineJoin should be defined'); + goog.asserts.assert(lineWidth !== undefined, 'lineWidth should be defined'); + goog.asserts.assert(miterLimit !== undefined, + 'miterLimit should be defined'); + if (state.currentStrokeStyle != strokeStyle || + state.currentLineCap != lineCap || + state.currentLineDash != lineDash || + state.currentLineJoin != lineJoin || + state.currentLineWidth != lineWidth || + state.currentMiterLimit != miterLimit) { + this.instructions.push( + [ol.render.canvas.Instruction.SET_STROKE_STYLE, + strokeStyle, lineWidth, lineCap, lineJoin, miterLimit, lineDash]); + state.currentStrokeStyle = strokeStyle; + state.currentLineCap = lineCap; + state.currentLineDash = lineDash; + state.currentLineJoin = lineJoin; + state.currentLineWidth = lineWidth; + state.currentMiterLimit = miterLimit; + } + } +}; + + +/** + * @constructor + * @extends {ol.render.canvas.Replay} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Maximum extent. + * @param {number} resolution Resolution. + * @protected + * @struct + */ +ol.render.canvas.TextReplay = function(tolerance, maxExtent, resolution) { + + ol.render.canvas.Replay.call(this, tolerance, maxExtent, resolution); + + /** + * @private + * @type {?ol.CanvasFillState} + */ + this.replayFillState_ = null; + + /** + * @private + * @type {?ol.CanvasStrokeState} + */ + this.replayStrokeState_ = null; + + /** + * @private + * @type {?ol.CanvasTextState} + */ + this.replayTextState_ = null; + + /** + * @private + * @type {string} + */ + this.text_ = ''; + + /** + * @private + * @type {number} + */ + this.textOffsetX_ = 0; + + /** + * @private + * @type {number} + */ + this.textOffsetY_ = 0; + + /** + * @private + * @type {number} + */ + this.textRotation_ = 0; + + /** + * @private + * @type {number} + */ + this.textScale_ = 0; + + /** + * @private + * @type {?ol.CanvasFillState} + */ + this.textFillState_ = null; + + /** + * @private + * @type {?ol.CanvasStrokeState} + */ + this.textStrokeState_ = null; + + /** + * @private + * @type {?ol.CanvasTextState} + */ + this.textState_ = null; + +}; +ol.inherits(ol.render.canvas.TextReplay, ol.render.canvas.Replay); + + +/** + * @inheritDoc + */ +ol.render.canvas.TextReplay.prototype.drawText = function(flatCoordinates, offset, end, stride, geometry, feature) { + if (this.text_ === '' || !this.textState_ || + (!this.textFillState_ && !this.textStrokeState_)) { + return; + } + if (this.textFillState_) { + this.setReplayFillState_(this.textFillState_); + } + if (this.textStrokeState_) { + this.setReplayStrokeState_(this.textStrokeState_); + } + this.setReplayTextState_(this.textState_); + this.beginGeometry(geometry, feature); + var myBegin = this.coordinates.length; + var myEnd = + this.appendFlatCoordinates(flatCoordinates, offset, end, stride, false); + var fill = !!this.textFillState_; + var stroke = !!this.textStrokeState_; + var drawTextInstruction = [ + ol.render.canvas.Instruction.DRAW_TEXT, myBegin, myEnd, this.text_, + this.textOffsetX_, this.textOffsetY_, this.textRotation_, this.textScale_, + fill, stroke]; + this.instructions.push(drawTextInstruction); + this.hitDetectionInstructions.push(drawTextInstruction); + this.endGeometry(geometry, feature); +}; + + +/** + * @param {ol.CanvasFillState} fillState Fill state. + * @private + */ +ol.render.canvas.TextReplay.prototype.setReplayFillState_ = function(fillState) { + var replayFillState = this.replayFillState_; + if (replayFillState && + replayFillState.fillStyle == fillState.fillStyle) { + return; + } + var setFillStyleInstruction = + [ol.render.canvas.Instruction.SET_FILL_STYLE, fillState.fillStyle]; + this.instructions.push(setFillStyleInstruction); + this.hitDetectionInstructions.push(setFillStyleInstruction); + if (!replayFillState) { + this.replayFillState_ = { + fillStyle: fillState.fillStyle + }; + } else { + replayFillState.fillStyle = fillState.fillStyle; + } +}; + + +/** + * @param {ol.CanvasStrokeState} strokeState Stroke state. + * @private + */ +ol.render.canvas.TextReplay.prototype.setReplayStrokeState_ = function(strokeState) { + var replayStrokeState = this.replayStrokeState_; + if (replayStrokeState && + replayStrokeState.lineCap == strokeState.lineCap && + replayStrokeState.lineDash == strokeState.lineDash && + replayStrokeState.lineJoin == strokeState.lineJoin && + replayStrokeState.lineWidth == strokeState.lineWidth && + replayStrokeState.miterLimit == strokeState.miterLimit && + replayStrokeState.strokeStyle == strokeState.strokeStyle) { + return; + } + var setStrokeStyleInstruction = [ + ol.render.canvas.Instruction.SET_STROKE_STYLE, strokeState.strokeStyle, + strokeState.lineWidth, strokeState.lineCap, strokeState.lineJoin, + strokeState.miterLimit, strokeState.lineDash, false + ]; + this.instructions.push(setStrokeStyleInstruction); + this.hitDetectionInstructions.push(setStrokeStyleInstruction); + if (!replayStrokeState) { + this.replayStrokeState_ = { + lineCap: strokeState.lineCap, + lineDash: strokeState.lineDash, + lineJoin: strokeState.lineJoin, + lineWidth: strokeState.lineWidth, + miterLimit: strokeState.miterLimit, + strokeStyle: strokeState.strokeStyle + }; + } else { + replayStrokeState.lineCap = strokeState.lineCap; + replayStrokeState.lineDash = strokeState.lineDash; + replayStrokeState.lineJoin = strokeState.lineJoin; + replayStrokeState.lineWidth = strokeState.lineWidth; + replayStrokeState.miterLimit = strokeState.miterLimit; + replayStrokeState.strokeStyle = strokeState.strokeStyle; + } +}; + + +/** + * @param {ol.CanvasTextState} textState Text state. + * @private + */ +ol.render.canvas.TextReplay.prototype.setReplayTextState_ = function(textState) { + var replayTextState = this.replayTextState_; + if (replayTextState && + replayTextState.font == textState.font && + replayTextState.textAlign == textState.textAlign && + replayTextState.textBaseline == textState.textBaseline) { + return; + } + var setTextStyleInstruction = [ol.render.canvas.Instruction.SET_TEXT_STYLE, + textState.font, textState.textAlign, textState.textBaseline]; + this.instructions.push(setTextStyleInstruction); + this.hitDetectionInstructions.push(setTextStyleInstruction); + if (!replayTextState) { + this.replayTextState_ = { + font: textState.font, + textAlign: textState.textAlign, + textBaseline: textState.textBaseline + }; + } else { + replayTextState.font = textState.font; + replayTextState.textAlign = textState.textAlign; + replayTextState.textBaseline = textState.textBaseline; + } +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.TextReplay.prototype.setTextStyle = function(textStyle) { + if (!textStyle) { + this.text_ = ''; + } else { + var textFillStyle = textStyle.getFill(); + if (!textFillStyle) { + this.textFillState_ = null; + } else { + var textFillStyleColor = textFillStyle.getColor(); + var fillStyle = ol.colorlike.asColorLike(textFillStyleColor ? + textFillStyleColor : ol.render.canvas.defaultFillStyle); + if (!this.textFillState_) { + this.textFillState_ = { + fillStyle: fillStyle + }; + } else { + var textFillState = this.textFillState_; + textFillState.fillStyle = fillStyle; + } + } + var textStrokeStyle = textStyle.getStroke(); + if (!textStrokeStyle) { + this.textStrokeState_ = null; + } else { + var textStrokeStyleColor = textStrokeStyle.getColor(); + var textStrokeStyleLineCap = textStrokeStyle.getLineCap(); + var textStrokeStyleLineDash = textStrokeStyle.getLineDash(); + var textStrokeStyleLineJoin = textStrokeStyle.getLineJoin(); + var textStrokeStyleWidth = textStrokeStyle.getWidth(); + var textStrokeStyleMiterLimit = textStrokeStyle.getMiterLimit(); + var lineCap = textStrokeStyleLineCap !== undefined ? + textStrokeStyleLineCap : ol.render.canvas.defaultLineCap; + var lineDash = textStrokeStyleLineDash ? + textStrokeStyleLineDash.slice() : ol.render.canvas.defaultLineDash; + var lineJoin = textStrokeStyleLineJoin !== undefined ? + textStrokeStyleLineJoin : ol.render.canvas.defaultLineJoin; + var lineWidth = textStrokeStyleWidth !== undefined ? + textStrokeStyleWidth : ol.render.canvas.defaultLineWidth; + var miterLimit = textStrokeStyleMiterLimit !== undefined ? + textStrokeStyleMiterLimit : ol.render.canvas.defaultMiterLimit; + var strokeStyle = ol.color.asString(textStrokeStyleColor ? + textStrokeStyleColor : ol.render.canvas.defaultStrokeStyle); + if (!this.textStrokeState_) { + this.textStrokeState_ = { + lineCap: lineCap, + lineDash: lineDash, + lineJoin: lineJoin, + lineWidth: lineWidth, + miterLimit: miterLimit, + strokeStyle: strokeStyle + }; + } else { + var textStrokeState = this.textStrokeState_; + textStrokeState.lineCap = lineCap; + textStrokeState.lineDash = lineDash; + textStrokeState.lineJoin = lineJoin; + textStrokeState.lineWidth = lineWidth; + textStrokeState.miterLimit = miterLimit; + textStrokeState.strokeStyle = strokeStyle; + } + } + var textFont = textStyle.getFont(); + var textOffsetX = textStyle.getOffsetX(); + var textOffsetY = textStyle.getOffsetY(); + var textRotation = textStyle.getRotation(); + var textScale = textStyle.getScale(); + var textText = textStyle.getText(); + var textTextAlign = textStyle.getTextAlign(); + var textTextBaseline = textStyle.getTextBaseline(); + var font = textFont !== undefined ? + textFont : ol.render.canvas.defaultFont; + var textAlign = textTextAlign !== undefined ? + textTextAlign : ol.render.canvas.defaultTextAlign; + var textBaseline = textTextBaseline !== undefined ? + textTextBaseline : ol.render.canvas.defaultTextBaseline; + if (!this.textState_) { + this.textState_ = { + font: font, + textAlign: textAlign, + textBaseline: textBaseline + }; + } else { + var textState = this.textState_; + textState.font = font; + textState.textAlign = textAlign; + textState.textBaseline = textBaseline; + } + this.text_ = textText !== undefined ? textText : ''; + this.textOffsetX_ = textOffsetX !== undefined ? textOffsetX : 0; + this.textOffsetY_ = textOffsetY !== undefined ? textOffsetY : 0; + this.textRotation_ = textRotation !== undefined ? textRotation : 0; + this.textScale_ = textScale !== undefined ? textScale : 1; + } +}; + + +/** + * @constructor + * @implements {ol.render.IReplayGroup} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Max extent. + * @param {number} resolution Resolution. + * @param {number=} opt_renderBuffer Optional rendering buffer. + * @struct + */ +ol.render.canvas.ReplayGroup = function( + tolerance, maxExtent, resolution, opt_renderBuffer) { + + /** + * @private + * @type {number} + */ + this.tolerance_ = tolerance; + + /** + * @private + * @type {ol.Extent} + */ + this.maxExtent_ = maxExtent; + + /** + * @private + * @type {number} + */ + this.resolution_ = resolution; + + /** + * @private + * @type {number|undefined} + */ + this.renderBuffer_ = opt_renderBuffer; + + /** + * @private + * @type {!Object.<string, + * Object.<ol.render.ReplayType, ol.render.canvas.Replay>>} + */ + this.replaysByZIndex_ = {}; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.hitDetectionContext_ = ol.dom.createCanvasContext2D(1, 1); + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.hitDetectionTransform_ = goog.vec.Mat4.createNumber(); + +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.render.canvas.ReplayGroup.prototype.finish = function() { + var zKey; + for (zKey in this.replaysByZIndex_) { + var replays = this.replaysByZIndex_[zKey]; + var replayKey; + for (replayKey in replays) { + replays[replayKey].finish(); + } + } +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T} callback Feature + * callback. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.canvas.ReplayGroup.prototype.forEachFeatureAtCoordinate = function( + coordinate, resolution, rotation, skippedFeaturesHash, callback) { + + var transform = this.hitDetectionTransform_; + ol.vec.Mat4.makeTransform2D(transform, 0.5, 0.5, + 1 / resolution, -1 / resolution, -rotation, + -coordinate[0], -coordinate[1]); + + var context = this.hitDetectionContext_; + context.clearRect(0, 0, 1, 1); + + /** + * @type {ol.Extent} + */ + var hitExtent; + if (this.renderBuffer_ !== undefined) { + hitExtent = ol.extent.createEmpty(); + ol.extent.extendCoordinate(hitExtent, coordinate); + ol.extent.buffer(hitExtent, resolution * this.renderBuffer_, hitExtent); + } + + return this.replayHitDetection_(context, transform, rotation, + skippedFeaturesHash, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + var imageData = context.getImageData(0, 0, 1, 1).data; + if (imageData[3] > 0) { + var result = callback(feature); + if (result) { + return result; + } + context.clearRect(0, 0, 1, 1); + } + }, hitExtent); +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.ReplayGroup.prototype.getReplay = function(zIndex, replayType) { + var zIndexKey = zIndex !== undefined ? zIndex.toString() : '0'; + var replays = this.replaysByZIndex_[zIndexKey]; + if (replays === undefined) { + replays = {}; + this.replaysByZIndex_[zIndexKey] = replays; + } + var replay = replays[replayType]; + if (replay === undefined) { + var Constructor = ol.render.canvas.BATCH_CONSTRUCTORS_[replayType]; + goog.asserts.assert(Constructor !== undefined, + replayType + + ' constructor missing from ol.render.canvas.BATCH_CONSTRUCTORS_'); + replay = new Constructor(this.tolerance_, this.maxExtent_, + this.resolution_); + replays[replayType] = replay; + } + return replay; +}; + + +/** + * @inheritDoc + */ +ol.render.canvas.ReplayGroup.prototype.isEmpty = function() { + return ol.object.isEmpty(this.replaysByZIndex_); +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {number} pixelRatio Pixel ratio. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {Array.<ol.render.ReplayType>=} opt_replayTypes Ordered replay types + * to replay. Default is {@link ol.render.REPLAY_ORDER} + */ +ol.render.canvas.ReplayGroup.prototype.replay = function(context, pixelRatio, + transform, viewRotation, skippedFeaturesHash, opt_replayTypes) { + + /** @type {Array.<number>} */ + var zs = Object.keys(this.replaysByZIndex_).map(Number); + zs.sort(ol.array.numberSafeCompareFunction); + + // setup clipping so that the parts of over-simplified geometries are not + // visible outside the current extent when panning + var maxExtent = this.maxExtent_; + var minX = maxExtent[0]; + var minY = maxExtent[1]; + var maxX = maxExtent[2]; + var maxY = maxExtent[3]; + var flatClipCoords = [minX, minY, minX, maxY, maxX, maxY, maxX, minY]; + ol.geom.flat.transform.transform2D( + flatClipCoords, 0, 8, 2, transform, flatClipCoords); + context.save(); + context.beginPath(); + context.moveTo(flatClipCoords[0], flatClipCoords[1]); + context.lineTo(flatClipCoords[2], flatClipCoords[3]); + context.lineTo(flatClipCoords[4], flatClipCoords[5]); + context.lineTo(flatClipCoords[6], flatClipCoords[7]); + context.closePath(); + context.clip(); + + var replayTypes = opt_replayTypes ? opt_replayTypes : ol.render.REPLAY_ORDER; + var i, ii, j, jj, replays, replay; + for (i = 0, ii = zs.length; i < ii; ++i) { + replays = this.replaysByZIndex_[zs[i].toString()]; + for (j = 0, jj = replayTypes.length; j < jj; ++j) { + replay = replays[replayTypes[j]]; + if (replay !== undefined) { + replay.replay(context, pixelRatio, transform, viewRotation, + skippedFeaturesHash); + } + } + } + + context.restore(); +}; + + +/** + * @private + * @param {CanvasRenderingContext2D} context Context. + * @param {goog.vec.Mat4.Number} transform Transform. + * @param {number} viewRotation View rotation. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T} featureCallback + * Feature callback. + * @param {ol.Extent=} opt_hitExtent Only check features that intersect this + * extent. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.canvas.ReplayGroup.prototype.replayHitDetection_ = function( + context, transform, viewRotation, skippedFeaturesHash, + featureCallback, opt_hitExtent) { + /** @type {Array.<number>} */ + var zs = Object.keys(this.replaysByZIndex_).map(Number); + zs.sort(function(a, b) { + return b - a; + }); + + var i, ii, j, replays, replay, result; + for (i = 0, ii = zs.length; i < ii; ++i) { + replays = this.replaysByZIndex_[zs[i].toString()]; + for (j = ol.render.REPLAY_ORDER.length - 1; j >= 0; --j) { + replay = replays[ol.render.REPLAY_ORDER[j]]; + if (replay !== undefined) { + result = replay.replayHitDetection(context, transform, viewRotation, + skippedFeaturesHash, featureCallback, opt_hitExtent); + if (result) { + return result; + } + } + } + } + return undefined; +}; + + +/** + * @const + * @private + * @type {Object.<ol.render.ReplayType, + * function(new: ol.render.canvas.Replay, number, ol.Extent, + * number)>} + */ +ol.render.canvas.BATCH_CONSTRUCTORS_ = { + 'Image': ol.render.canvas.ImageReplay, + 'LineString': ol.render.canvas.LineStringReplay, + 'Polygon': ol.render.canvas.PolygonReplay, + 'Text': ol.render.canvas.TextReplay +}; + +goog.provide('ol.render.Feature'); + + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryType'); + + +/** + * Lightweight, read-only, {@link ol.Feature} and {@link ol.geom.Geometry} like + * structure, optimized for rendering and styling. Geometry access through the + * API is limited to getting the type and extent of the geometry. + * + * @constructor + * @param {ol.geom.GeometryType} type Geometry type. + * @param {Array.<number>} flatCoordinates Flat coordinates. These always need + * to be right-handed for polygons. + * @param {Array.<number>|Array.<Array.<number>>} ends Ends or Endss. + * @param {Object.<string, *>} properties Properties. + */ +ol.render.Feature = function(type, flatCoordinates, ends, properties) { + + /** + * @private + * @type {ol.Extent|undefined} + */ + this.extent_; + + goog.asserts.assert(type === ol.geom.GeometryType.POINT || + type === ol.geom.GeometryType.MULTI_POINT || + type === ol.geom.GeometryType.LINE_STRING || + type === ol.geom.GeometryType.MULTI_LINE_STRING || + type === ol.geom.GeometryType.POLYGON, + 'Need a Point, MultiPoint, LineString, MultiLineString or Polygon type'); + + /** + * @private + * @type {ol.geom.GeometryType} + */ + this.type_ = type; + + /** + * @private + * @type {Array.<number>} + */ + this.flatCoordinates_ = flatCoordinates; + + /** + * @private + * @type {Array.<number>|Array.<Array.<number>>} + */ + this.ends_ = ends; + + /** + * @private + * @type {Object.<string, *>} + */ + this.properties_ = properties; + +}; + + +/** + * Get a feature property by its key. + * @param {string} key Key + * @return {*} Value for the requested key. + * @api + */ +ol.render.Feature.prototype.get = function(key) { + return this.properties_[key]; +}; + + +/** + * @return {Array.<number>|Array.<Array.<number>>} Ends or endss. + */ +ol.render.Feature.prototype.getEnds = function() { + return this.ends_; +}; + + +/** + * Get the extent of this feature's geometry. + * @return {ol.Extent} Extent. + * @api + */ +ol.render.Feature.prototype.getExtent = function() { + if (!this.extent_) { + this.extent_ = this.type_ === ol.geom.GeometryType.POINT ? + ol.extent.createOrUpdateFromCoordinate(this.flatCoordinates_) : + ol.extent.createOrUpdateFromFlatCoordinates( + this.flatCoordinates_, 0, this.flatCoordinates_.length, 2); + + } + return this.extent_; +}; + + +/** + * @return {Array.<number>} Flat coordinates. + */ +ol.render.Feature.prototype.getOrientedFlatCoordinates = function() { + return this.flatCoordinates_; +}; + + +/** + * @return {Array.<number>} Flat coordinates. + */ +ol.render.Feature.prototype.getFlatCoordinates = + ol.render.Feature.prototype.getOrientedFlatCoordinates; + + +/** + * Get the feature for working with its geometry. + * @return {ol.render.Feature} Feature. + * @api + */ +ol.render.Feature.prototype.getGeometry = function() { + return this; +}; + + +/** + * Get the feature properties. + * @return {Object.<string, *>} Feature properties. + * @api + */ +ol.render.Feature.prototype.getProperties = function() { + return this.properties_; +}; + + +/** + * Get the feature for working with its geometry. + * @return {ol.render.Feature} Feature. + */ +ol.render.Feature.prototype.getSimplifiedGeometry = + ol.render.Feature.prototype.getGeometry; + + +/** + * @return {number} Stride. + */ +ol.render.Feature.prototype.getStride = function() { + return 2; +}; + + +/** + * @return {undefined} + */ +ol.render.Feature.prototype.getStyleFunction = ol.nullFunction; + + +/** + * Get the type of this feature's geometry. + * @return {ol.geom.GeometryType} Geometry type. + * @api + */ +ol.render.Feature.prototype.getType = function() { + return this.type_; +}; + +goog.provide('ol.renderer.vector'); + +goog.require('goog.asserts'); +goog.require('ol.render.Feature'); +goog.require('ol.render.IReplayGroup'); +goog.require('ol.style.ImageState'); +goog.require('ol.style.Style'); + + +/** + * @param {ol.Feature|ol.render.Feature} feature1 Feature 1. + * @param {ol.Feature|ol.render.Feature} feature2 Feature 2. + * @return {number} Order. + */ +ol.renderer.vector.defaultOrder = function(feature1, feature2) { + return goog.getUid(feature1) - goog.getUid(feature2); +}; + + +/** + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @return {number} Squared pixel tolerance. + */ +ol.renderer.vector.getSquaredTolerance = function(resolution, pixelRatio) { + var tolerance = ol.renderer.vector.getTolerance(resolution, pixelRatio); + return tolerance * tolerance; +}; + + +/** + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @return {number} Pixel tolerance. + */ +ol.renderer.vector.getTolerance = function(resolution, pixelRatio) { + return ol.SIMPLIFY_TOLERANCE * resolution / pixelRatio; +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.Circle} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderCircleGeometry_ = function(replayGroup, geometry, style, feature) { + var fillStyle = style.getFill(); + var strokeStyle = style.getStroke(); + if (fillStyle || strokeStyle) { + var polygonReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.POLYGON); + polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle); + polygonReplay.drawCircle(geometry, feature); + } + var textStyle = style.getText(); + if (textStyle) { + var textReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.TEXT); + textReplay.setTextStyle(textStyle); + textReplay.drawText(geometry.getCenter(), 0, 2, 2, geometry, feature); + } +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {ol.style.Style} style Style. + * @param {number} squaredTolerance Squared tolerance. + * @param {function(this: T, ol.events.Event)} listener Listener function. + * @param {T} thisArg Value to use as `this` when executing `listener`. + * @return {boolean} `true` if style is loading. + * @template T + */ +ol.renderer.vector.renderFeature = function( + replayGroup, feature, style, squaredTolerance, listener, thisArg) { + var loading = false; + var imageStyle, imageState; + imageStyle = style.getImage(); + if (imageStyle) { + imageState = imageStyle.getImageState(); + if (imageState == ol.style.ImageState.LOADED || + imageState == ol.style.ImageState.ERROR) { + imageStyle.unlistenImageChange(listener, thisArg); + } else { + if (imageState == ol.style.ImageState.IDLE) { + imageStyle.load(); + } + imageState = imageStyle.getImageState(); + goog.asserts.assert(imageState == ol.style.ImageState.LOADING, + 'imageState should be LOADING'); + imageStyle.listenImageChange(listener, thisArg); + loading = true; + } + } + ol.renderer.vector.renderFeature_(replayGroup, feature, style, + squaredTolerance); + return loading; +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {ol.style.Style} style Style. + * @param {number} squaredTolerance Squared tolerance. + * @private + */ +ol.renderer.vector.renderFeature_ = function( + replayGroup, feature, style, squaredTolerance) { + var geometry = style.getGeometryFunction()(feature); + if (!geometry) { + return; + } + var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance); + var geometryRenderer = + ol.renderer.vector.GEOMETRY_RENDERERS_[simplifiedGeometry.getType()]; + goog.asserts.assert(geometryRenderer !== undefined, + 'geometryRenderer should be defined'); + geometryRenderer(replayGroup, simplifiedGeometry, style, feature); +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.GeometryCollection} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderGeometryCollectionGeometry_ = function(replayGroup, geometry, style, feature) { + var geometries = geometry.getGeometriesArray(); + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + var geometryRenderer = + ol.renderer.vector.GEOMETRY_RENDERERS_[geometries[i].getType()]; + goog.asserts.assert(geometryRenderer !== undefined, + 'geometryRenderer should be defined'); + geometryRenderer(replayGroup, geometries[i], style, feature); + } +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.LineString|ol.render.Feature} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderLineStringGeometry_ = function(replayGroup, geometry, style, feature) { + var strokeStyle = style.getStroke(); + if (strokeStyle) { + var lineStringReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.LINE_STRING); + lineStringReplay.setFillStrokeStyle(null, strokeStyle); + lineStringReplay.drawLineString(geometry, feature); + } + var textStyle = style.getText(); + if (textStyle) { + var textReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.TEXT); + textReplay.setTextStyle(textStyle); + textReplay.drawText(geometry.getFlatMidpoint(), 0, 2, 2, geometry, feature); + } +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.MultiLineString|ol.render.Feature} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderMultiLineStringGeometry_ = function(replayGroup, geometry, style, feature) { + var strokeStyle = style.getStroke(); + if (strokeStyle) { + var lineStringReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.LINE_STRING); + lineStringReplay.setFillStrokeStyle(null, strokeStyle); + lineStringReplay.drawMultiLineString(geometry, feature); + } + var textStyle = style.getText(); + if (textStyle) { + var textReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.TEXT); + textReplay.setTextStyle(textStyle); + var flatMidpointCoordinates = geometry.getFlatMidpoints(); + textReplay.drawText(flatMidpointCoordinates, 0, + flatMidpointCoordinates.length, 2, geometry, feature); + } +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.MultiPolygon} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderMultiPolygonGeometry_ = function(replayGroup, geometry, style, feature) { + var fillStyle = style.getFill(); + var strokeStyle = style.getStroke(); + if (strokeStyle || fillStyle) { + var polygonReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.POLYGON); + polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle); + polygonReplay.drawMultiPolygon(geometry, feature); + } + var textStyle = style.getText(); + if (textStyle) { + var textReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.TEXT); + textReplay.setTextStyle(textStyle); + var flatInteriorPointCoordinates = geometry.getFlatInteriorPoints(); + textReplay.drawText(flatInteriorPointCoordinates, 0, + flatInteriorPointCoordinates.length, 2, geometry, feature); + } +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.Point|ol.render.Feature} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderPointGeometry_ = function(replayGroup, geometry, style, feature) { + var imageStyle = style.getImage(); + if (imageStyle) { + if (imageStyle.getImageState() != ol.style.ImageState.LOADED) { + return; + } + var imageReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.IMAGE); + imageReplay.setImageStyle(imageStyle); + imageReplay.drawPoint(geometry, feature); + } + var textStyle = style.getText(); + if (textStyle) { + var textReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.TEXT); + textReplay.setTextStyle(textStyle); + textReplay.drawText(geometry.getFlatCoordinates(), 0, 2, 2, geometry, + feature); + } +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.MultiPoint|ol.render.Feature} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderMultiPointGeometry_ = function(replayGroup, geometry, style, feature) { + var imageStyle = style.getImage(); + if (imageStyle) { + if (imageStyle.getImageState() != ol.style.ImageState.LOADED) { + return; + } + var imageReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.IMAGE); + imageReplay.setImageStyle(imageStyle); + imageReplay.drawMultiPoint(geometry, feature); + } + var textStyle = style.getText(); + if (textStyle) { + var textReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.TEXT); + textReplay.setTextStyle(textStyle); + var flatCoordinates = geometry.getFlatCoordinates(); + textReplay.drawText(flatCoordinates, 0, flatCoordinates.length, + geometry.getStride(), geometry, feature); + } +}; + + +/** + * @param {ol.render.IReplayGroup} replayGroup Replay group. + * @param {ol.geom.Polygon|ol.render.Feature} geometry Geometry. + * @param {ol.style.Style} style Style. + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @private + */ +ol.renderer.vector.renderPolygonGeometry_ = function(replayGroup, geometry, style, feature) { + var fillStyle = style.getFill(); + var strokeStyle = style.getStroke(); + if (fillStyle || strokeStyle) { + var polygonReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.POLYGON); + polygonReplay.setFillStrokeStyle(fillStyle, strokeStyle); + polygonReplay.drawPolygon(geometry, feature); + } + var textStyle = style.getText(); + if (textStyle) { + var textReplay = replayGroup.getReplay( + style.getZIndex(), ol.render.ReplayType.TEXT); + textReplay.setTextStyle(textStyle); + textReplay.drawText( + geometry.getFlatInteriorPoint(), 0, 2, 2, geometry, feature); + } +}; + + +/** + * @const + * @private + * @type {Object.<ol.geom.GeometryType, + * function(ol.render.IReplayGroup, ol.geom.Geometry, + * ol.style.Style, Object)>} + */ +ol.renderer.vector.GEOMETRY_RENDERERS_ = { + 'Point': ol.renderer.vector.renderPointGeometry_, + 'LineString': ol.renderer.vector.renderLineStringGeometry_, + 'Polygon': ol.renderer.vector.renderPolygonGeometry_, + 'MultiPoint': ol.renderer.vector.renderMultiPointGeometry_, + 'MultiLineString': ol.renderer.vector.renderMultiLineStringGeometry_, + 'MultiPolygon': ol.renderer.vector.renderMultiPolygonGeometry_, + 'GeometryCollection': ol.renderer.vector.renderGeometryCollectionGeometry_, + 'Circle': ol.renderer.vector.renderCircleGeometry_ +}; + +goog.provide('ol.ImageCanvas'); + +goog.require('goog.asserts'); +goog.require('ol.ImageBase'); +goog.require('ol.ImageState'); + + +/** + * @constructor + * @extends {ol.ImageBase} + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {Array.<ol.Attribution>} attributions Attributions. + * @param {HTMLCanvasElement} canvas Canvas. + * @param {ol.ImageCanvasLoader=} opt_loader Optional loader function to + * support asynchronous canvas drawing. + */ +ol.ImageCanvas = function(extent, resolution, pixelRatio, attributions, + canvas, opt_loader) { + + /** + * Optional canvas loader function. + * @type {?ol.ImageCanvasLoader} + * @private + */ + this.loader_ = opt_loader !== undefined ? opt_loader : null; + + var state = opt_loader !== undefined ? + ol.ImageState.IDLE : ol.ImageState.LOADED; + + ol.ImageBase.call(this, extent, resolution, pixelRatio, state, attributions); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = canvas; + + /** + * @private + * @type {Error} + */ + this.error_ = null; + +}; +ol.inherits(ol.ImageCanvas, ol.ImageBase); + + +/** + * Get any error associated with asynchronous rendering. + * @return {Error} Any error that occurred during rendering. + */ +ol.ImageCanvas.prototype.getError = function() { + return this.error_; +}; + + +/** + * Handle async drawing complete. + * @param {Error} err Any error during drawing. + * @private + */ +ol.ImageCanvas.prototype.handleLoad_ = function(err) { + if (err) { + this.error_ = err; + this.state = ol.ImageState.ERROR; + } else { + this.state = ol.ImageState.LOADED; + } + this.changed(); +}; + + +/** + * Trigger drawing on canvas. + */ +ol.ImageCanvas.prototype.load = function() { + if (this.state == ol.ImageState.IDLE) { + goog.asserts.assert(this.loader_, 'this.loader_ must be set'); + this.state = ol.ImageState.LOADING; + this.changed(); + this.loader_(this.handleLoad_.bind(this)); + } +}; + + +/** + * @inheritDoc + */ +ol.ImageCanvas.prototype.getImage = function(opt_context) { + return this.canvas_; +}; + +goog.provide('ol.reproj'); + +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.math'); +goog.require('ol.proj'); + + +/** + * We need to employ more sophisticated solution + * if the web browser antialiases clipping edges on canvas. + * + * Currently only Chrome does not antialias the edges, but this is probably + * going to be "fixed" in the future: http://crbug.com/424291 + * + * @type {boolean} + * @private + */ +ol.reproj.browserAntialiasesClip_ = (function(winNav, winChrome) { + // Adapted from http://stackoverflow.com/questions/4565112/javascript-how-to-find-out-if-the-user-browser-is-chrome + var isOpera = winNav.userAgent.indexOf('OPR') > -1; + var isIEedge = winNav.userAgent.indexOf('Edge') > -1; + return !( + !winNav.userAgent.match('CriOS') && // Not Chrome on iOS + winChrome !== null && winChrome !== undefined && // Has chrome in window + winNav.vendor === 'Google Inc.' && // Vendor is Google. + isOpera == false && // Not Opera + isIEedge == false // Not Edge + ); +})(ol.global.navigator, ol.global.chrome); + + +/** + * Calculates ideal resolution to use from the source in order to achieve + * pixel mapping as close as possible to 1:1 during reprojection. + * The resolution is calculated regardless of what resolutions + * are actually available in the dataset (TileGrid, Image, ...). + * + * @param {ol.proj.Projection} sourceProj Source projection. + * @param {ol.proj.Projection} targetProj Target projection. + * @param {ol.Coordinate} targetCenter Target center. + * @param {number} targetResolution Target resolution. + * @return {number} The best resolution to use. Can be +-Infinity, NaN or 0. + */ +ol.reproj.calculateSourceResolution = function(sourceProj, targetProj, + targetCenter, targetResolution) { + + var sourceCenter = ol.proj.transform(targetCenter, targetProj, sourceProj); + + // calculate the ideal resolution of the source data + var sourceResolution = + targetProj.getPointResolution(targetResolution, targetCenter); + + var targetMetersPerUnit = targetProj.getMetersPerUnit(); + if (targetMetersPerUnit !== undefined) { + sourceResolution *= targetMetersPerUnit; + } + var sourceMetersPerUnit = sourceProj.getMetersPerUnit(); + if (sourceMetersPerUnit !== undefined) { + sourceResolution /= sourceMetersPerUnit; + } + + // Based on the projection properties, the point resolution at the specified + // coordinates may be slightly different. We need to reverse-compensate this + // in order to achieve optimal results. + + var compensationFactor = + sourceProj.getPointResolution(sourceResolution, sourceCenter) / + sourceResolution; + + if (isFinite(compensationFactor) && compensationFactor > 0) { + sourceResolution /= compensationFactor; + } + + return sourceResolution; +}; + + +/** + * Enlarge the clipping triangle point by 1 pixel to ensure the edges overlap + * in order to mask gaps caused by antialiasing. + * + * @param {number} centroidX Centroid of the triangle (x coordinate in pixels). + * @param {number} centroidY Centroid of the triangle (y coordinate in pixels). + * @param {number} x X coordinate of the point (in pixels). + * @param {number} y Y coordinate of the point (in pixels). + * @return {ol.Coordinate} New point 1 px farther from the centroid. + * @private + */ +ol.reproj.enlargeClipPoint_ = function(centroidX, centroidY, x, y) { + var dX = x - centroidX, dY = y - centroidY; + var distance = Math.sqrt(dX * dX + dY * dY); + return [Math.round(x + dX / distance), Math.round(y + dY / distance)]; +}; + + +/** + * Renders the source data into new canvas based on the triangulation. + * + * @param {number} width Width of the canvas. + * @param {number} height Height of the canvas. + * @param {number} pixelRatio Pixel ratio. + * @param {number} sourceResolution Source resolution. + * @param {ol.Extent} sourceExtent Extent of the data source. + * @param {number} targetResolution Target resolution. + * @param {ol.Extent} targetExtent Target extent. + * @param {ol.reproj.Triangulation} triangulation Calculated triangulation. + * @param {Array.<{extent: ol.Extent, + * image: (HTMLCanvasElement|Image|HTMLVideoElement)}>} sources + * Array of sources. + * @param {number} gutter Gutter of the sources. + * @param {boolean=} opt_renderEdges Render reprojection edges. + * @return {HTMLCanvasElement} Canvas with reprojected data. + */ +ol.reproj.render = function(width, height, pixelRatio, + sourceResolution, sourceExtent, targetResolution, targetExtent, + triangulation, sources, gutter, opt_renderEdges) { + + var context = ol.dom.createCanvasContext2D(Math.round(pixelRatio * width), + Math.round(pixelRatio * height)); + + if (sources.length === 0) { + return context.canvas; + } + + context.scale(pixelRatio, pixelRatio); + + var sourceDataExtent = ol.extent.createEmpty(); + sources.forEach(function(src, i, arr) { + ol.extent.extend(sourceDataExtent, src.extent); + }); + + var canvasWidthInUnits = ol.extent.getWidth(sourceDataExtent); + var canvasHeightInUnits = ol.extent.getHeight(sourceDataExtent); + var stitchContext = ol.dom.createCanvasContext2D( + Math.round(pixelRatio * canvasWidthInUnits / sourceResolution), + Math.round(pixelRatio * canvasHeightInUnits / sourceResolution)); + + var stitchScale = pixelRatio / sourceResolution; + + sources.forEach(function(src, i, arr) { + var xPos = src.extent[0] - sourceDataExtent[0]; + var yPos = -(src.extent[3] - sourceDataExtent[3]); + var srcWidth = ol.extent.getWidth(src.extent); + var srcHeight = ol.extent.getHeight(src.extent); + + stitchContext.drawImage( + src.image, + gutter, gutter, + src.image.width - 2 * gutter, src.image.height - 2 * gutter, + xPos * stitchScale, yPos * stitchScale, + srcWidth * stitchScale, srcHeight * stitchScale); + }); + + var targetTopLeft = ol.extent.getTopLeft(targetExtent); + + triangulation.getTriangles().forEach(function(triangle, i, arr) { + /* Calculate affine transform (src -> dst) + * Resulting matrix can be used to transform coordinate + * from `sourceProjection` to destination pixels. + * + * To optimize number of context calls and increase numerical stability, + * we also do the following operations: + * trans(-topLeftExtentCorner), scale(1 / targetResolution), scale(1, -1) + * here before solving the linear system so [ui, vi] are pixel coordinates. + * + * Src points: xi, yi + * Dst points: ui, vi + * Affine coefficients: aij + * + * | x0 y0 1 0 0 0 | |a00| |u0| + * | x1 y1 1 0 0 0 | |a01| |u1| + * | x2 y2 1 0 0 0 | x |a02| = |u2| + * | 0 0 0 x0 y0 1 | |a10| |v0| + * | 0 0 0 x1 y1 1 | |a11| |v1| + * | 0 0 0 x2 y2 1 | |a12| |v2| + */ + var source = triangle.source, target = triangle.target; + var x0 = source[0][0], y0 = source[0][1], + x1 = source[1][0], y1 = source[1][1], + x2 = source[2][0], y2 = source[2][1]; + var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution, + v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution; + var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution, + v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution; + var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution, + v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution; + + // Shift all the source points to improve numerical stability + // of all the subsequent calculations. The [x0, y0] is used here. + // This is also used to simplify the linear system. + var sourceNumericalShiftX = x0, sourceNumericalShiftY = y0; + x0 = 0; + y0 = 0; + x1 -= sourceNumericalShiftX; + y1 -= sourceNumericalShiftY; + x2 -= sourceNumericalShiftX; + y2 -= sourceNumericalShiftY; + + var augmentedMatrix = [ + [x1, y1, 0, 0, u1 - u0], + [x2, y2, 0, 0, u2 - u0], + [0, 0, x1, y1, v1 - v0], + [0, 0, x2, y2, v2 - v0] + ]; + var affineCoefs = ol.math.solveLinearSystem(augmentedMatrix); + if (!affineCoefs) { + return; + } + + context.save(); + context.beginPath(); + if (ol.reproj.browserAntialiasesClip_) { + var centroidX = (u0 + u1 + u2) / 3, centroidY = (v0 + v1 + v2) / 3; + var p0 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u0, v0); + var p1 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u1, v1); + var p2 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u2, v2); + + context.moveTo(p0[0], p0[1]); + context.lineTo(p1[0], p1[1]); + context.lineTo(p2[0], p2[1]); + } else { + context.moveTo(u0, v0); + context.lineTo(u1, v1); + context.lineTo(u2, v2); + } + context.closePath(); + context.clip(); + + context.transform( + affineCoefs[0], affineCoefs[2], affineCoefs[1], affineCoefs[3], u0, v0); + + context.translate(sourceDataExtent[0] - sourceNumericalShiftX, + sourceDataExtent[3] - sourceNumericalShiftY); + + context.scale(sourceResolution / pixelRatio, + -sourceResolution / pixelRatio); + + context.drawImage(stitchContext.canvas, 0, 0); + context.restore(); + }); + + if (opt_renderEdges) { + context.save(); + + context.strokeStyle = 'black'; + context.lineWidth = 1; + + triangulation.getTriangles().forEach(function(triangle, i, arr) { + var target = triangle.target; + var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution, + v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution; + var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution, + v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution; + var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution, + v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution; + + context.beginPath(); + context.moveTo(u0, v0); + context.lineTo(u1, v1); + context.lineTo(u2, v2); + context.closePath(); + context.stroke(); + }); + + context.restore(); + } + return context.canvas; +}; + +goog.provide('ol.reproj.Triangulation'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.math'); +goog.require('ol.proj'); + + +/** + * @classdesc + * Class containing triangulation of the given target extent. + * Used for determining source data and the reprojection itself. + * + * @param {ol.proj.Projection} sourceProj Source projection. + * @param {ol.proj.Projection} targetProj Target projection. + * @param {ol.Extent} targetExtent Target extent to triangulate. + * @param {ol.Extent} maxSourceExtent Maximal source extent that can be used. + * @param {number} errorThreshold Acceptable error (in source units). + * @constructor + */ +ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent, + maxSourceExtent, errorThreshold) { + + /** + * @type {ol.proj.Projection} + * @private + */ + this.sourceProj_ = sourceProj; + + /** + * @type {ol.proj.Projection} + * @private + */ + this.targetProj_ = targetProj; + + /** @type {!Object.<string, ol.Coordinate>} */ + var transformInvCache = {}; + var transformInv = ol.proj.getTransform(this.targetProj_, this.sourceProj_); + + /** + * @param {ol.Coordinate} c A coordinate. + * @return {ol.Coordinate} Transformed coordinate. + * @private + */ + this.transformInv_ = function(c) { + var key = c[0] + '/' + c[1]; + if (!transformInvCache[key]) { + transformInvCache[key] = transformInv(c); + } + return transformInvCache[key]; + }; + + /** + * @type {ol.Extent} + * @private + */ + this.maxSourceExtent_ = maxSourceExtent; + + /** + * @type {number} + * @private + */ + this.errorThresholdSquared_ = errorThreshold * errorThreshold; + + /** + * @type {Array.<ol.ReprojTriangle>} + * @private + */ + this.triangles_ = []; + + /** + * Indicates that the triangulation crosses edge of the source projection. + * @type {boolean} + * @private + */ + this.wrapsXInSource_ = false; + + /** + * @type {boolean} + * @private + */ + this.canWrapXInSource_ = this.sourceProj_.canWrapX() && + !!maxSourceExtent && + !!this.sourceProj_.getExtent() && + (ol.extent.getWidth(maxSourceExtent) == + ol.extent.getWidth(this.sourceProj_.getExtent())); + + /** + * @type {?number} + * @private + */ + this.sourceWorldWidth_ = this.sourceProj_.getExtent() ? + ol.extent.getWidth(this.sourceProj_.getExtent()) : null; + + /** + * @type {?number} + * @private + */ + this.targetWorldWidth_ = this.targetProj_.getExtent() ? + ol.extent.getWidth(this.targetProj_.getExtent()) : null; + + var destinationTopLeft = ol.extent.getTopLeft(targetExtent); + var destinationTopRight = ol.extent.getTopRight(targetExtent); + var destinationBottomRight = ol.extent.getBottomRight(targetExtent); + var destinationBottomLeft = ol.extent.getBottomLeft(targetExtent); + var sourceTopLeft = this.transformInv_(destinationTopLeft); + var sourceTopRight = this.transformInv_(destinationTopRight); + var sourceBottomRight = this.transformInv_(destinationBottomRight); + var sourceBottomLeft = this.transformInv_(destinationBottomLeft); + + this.addQuad_( + destinationTopLeft, destinationTopRight, + destinationBottomRight, destinationBottomLeft, + sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft, + ol.RASTER_REPROJECTION_MAX_SUBDIVISION); + + if (this.wrapsXInSource_) { + // Fix coordinates (ol.proj returns wrapped coordinates, "unwrap" here). + // This significantly simplifies the rest of the reprojection process. + + goog.asserts.assert(this.sourceWorldWidth_ !== null); + var leftBound = Infinity; + this.triangles_.forEach(function(triangle, i, arr) { + leftBound = Math.min(leftBound, + triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]); + }); + + // Shift triangles to be as close to `leftBound` as possible + // (if the distance is more than `worldWidth / 2` it can be closer. + this.triangles_.forEach(function(triangle) { + if (Math.max(triangle.source[0][0], triangle.source[1][0], + triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) { + var newTriangle = [[triangle.source[0][0], triangle.source[0][1]], + [triangle.source[1][0], triangle.source[1][1]], + [triangle.source[2][0], triangle.source[2][1]]]; + if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) { + newTriangle[0][0] -= this.sourceWorldWidth_; + } + if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) { + newTriangle[1][0] -= this.sourceWorldWidth_; + } + if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) { + newTriangle[2][0] -= this.sourceWorldWidth_; + } + + // Rarely (if the extent contains both the dateline and prime meridian) + // the shift can in turn break some triangles. + // Detect this here and don't shift in such cases. + var minX = Math.min( + newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); + var maxX = Math.max( + newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]); + if ((maxX - minX) < this.sourceWorldWidth_ / 2) { + triangle.source = newTriangle; + } + } + }, this); + } + + transformInvCache = {}; +}; + + +/** + * Adds triangle to the triangulation. + * @param {ol.Coordinate} a The target a coordinate. + * @param {ol.Coordinate} b The target b coordinate. + * @param {ol.Coordinate} c The target c coordinate. + * @param {ol.Coordinate} aSrc The source a coordinate. + * @param {ol.Coordinate} bSrc The source b coordinate. + * @param {ol.Coordinate} cSrc The source c coordinate. + * @private + */ +ol.reproj.Triangulation.prototype.addTriangle_ = function(a, b, c, + aSrc, bSrc, cSrc) { + this.triangles_.push({ + source: [aSrc, bSrc, cSrc], + target: [a, b, c] + }); +}; + + +/** + * Adds quad (points in clock-wise order) to the triangulation + * (and reprojects the vertices) if valid. + * Performs quad subdivision if needed to increase precision. + * + * @param {ol.Coordinate} a The target a coordinate. + * @param {ol.Coordinate} b The target b coordinate. + * @param {ol.Coordinate} c The target c coordinate. + * @param {ol.Coordinate} d The target d coordinate. + * @param {ol.Coordinate} aSrc The source a coordinate. + * @param {ol.Coordinate} bSrc The source b coordinate. + * @param {ol.Coordinate} cSrc The source c coordinate. + * @param {ol.Coordinate} dSrc The source d coordinate. + * @param {number} maxSubdivision Maximal allowed subdivision of the quad. + * @private + */ +ol.reproj.Triangulation.prototype.addQuad_ = function(a, b, c, d, + aSrc, bSrc, cSrc, dSrc, maxSubdivision) { + + var sourceQuadExtent = ol.extent.boundingExtent([aSrc, bSrc, cSrc, dSrc]); + var sourceCoverageX = this.sourceWorldWidth_ ? + ol.extent.getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null; + + // when the quad is wrapped in the source projection + // it covers most of the projection extent, but not fully + var wrapsX = this.sourceProj_.canWrapX() && + sourceCoverageX > 0.5 && sourceCoverageX < 1; + + var needsSubdivision = false; + + if (maxSubdivision > 0) { + if (this.targetProj_.isGlobal() && this.targetWorldWidth_) { + var targetQuadExtent = ol.extent.boundingExtent([a, b, c, d]); + var targetCoverageX = + ol.extent.getWidth(targetQuadExtent) / this.targetWorldWidth_; + needsSubdivision |= + targetCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH; + } + if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) { + needsSubdivision |= + sourceCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH; + } + } + + if (!needsSubdivision && this.maxSourceExtent_) { + if (!ol.extent.intersects(sourceQuadExtent, this.maxSourceExtent_)) { + // whole quad outside source projection extent -> ignore + return; + } + } + + if (!needsSubdivision) { + if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) || + !isFinite(bSrc[0]) || !isFinite(bSrc[1]) || + !isFinite(cSrc[0]) || !isFinite(cSrc[1]) || + !isFinite(dSrc[0]) || !isFinite(dSrc[1])) { + if (maxSubdivision > 0) { + needsSubdivision = true; + } else { + return; + } + } + } + + if (maxSubdivision > 0) { + if (!needsSubdivision) { + var center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2]; + var centerSrc = this.transformInv_(center); + + var dx; + if (wrapsX) { + goog.asserts.assert(this.sourceWorldWidth_); + var centerSrcEstimX = + (ol.math.modulo(aSrc[0], this.sourceWorldWidth_) + + ol.math.modulo(cSrc[0], this.sourceWorldWidth_)) / 2; + dx = centerSrcEstimX - + ol.math.modulo(centerSrc[0], this.sourceWorldWidth_); + } else { + dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0]; + } + var dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1]; + var centerSrcErrorSquared = dx * dx + dy * dy; + needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_; + } + if (needsSubdivision) { + if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) { + // split horizontally (top & bottom) + var bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2]; + var bcSrc = this.transformInv_(bc); + var da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2]; + var daSrc = this.transformInv_(da); + + this.addQuad_( + a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1); + this.addQuad_( + da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1); + } else { + // split vertically (left & right) + var ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2]; + var abSrc = this.transformInv_(ab); + var cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2]; + var cdSrc = this.transformInv_(cd); + + this.addQuad_( + a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1); + this.addQuad_( + ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1); + } + return; + } + } + + if (wrapsX) { + if (!this.canWrapXInSource_) { + return; + } + this.wrapsXInSource_ = true; + } + + this.addTriangle_(a, c, d, aSrc, cSrc, dSrc); + this.addTriangle_(a, b, c, aSrc, bSrc, cSrc); +}; + + +/** + * Calculates extent of the 'source' coordinates from all the triangles. + * + * @return {ol.Extent} Calculated extent. + */ +ol.reproj.Triangulation.prototype.calculateSourceExtent = function() { + var extent = ol.extent.createEmpty(); + + this.triangles_.forEach(function(triangle, i, arr) { + var src = triangle.source; + ol.extent.extendCoordinate(extent, src[0]); + ol.extent.extendCoordinate(extent, src[1]); + ol.extent.extendCoordinate(extent, src[2]); + }); + + return extent; +}; + + +/** + * @return {Array.<ol.ReprojTriangle>} Array of the calculated triangles. + */ +ol.reproj.Triangulation.prototype.getTriangles = function() { + return this.triangles_; +}; + +goog.provide('ol.reproj.Image'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.ImageBase'); +goog.require('ol.ImageState'); +goog.require('ol.extent'); +goog.require('ol.proj'); +goog.require('ol.reproj'); +goog.require('ol.reproj.Triangulation'); + + +/** + * @classdesc + * Class encapsulating single reprojected image. + * See {@link ol.source.Image}. + * + * @constructor + * @extends {ol.ImageBase} + * @param {ol.proj.Projection} sourceProj Source projection (of the data). + * @param {ol.proj.Projection} targetProj Target projection. + * @param {ol.Extent} targetExtent Target extent. + * @param {number} targetResolution Target resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.ReprojImageFunctionType} getImageFunction + * Function returning source images (extent, resolution, pixelRatio). + */ +ol.reproj.Image = function(sourceProj, targetProj, + targetExtent, targetResolution, pixelRatio, getImageFunction) { + + /** + * @private + * @type {ol.proj.Projection} + */ + this.targetProj_ = targetProj; + + /** + * @private + * @type {ol.Extent} + */ + this.maxSourceExtent_ = sourceProj.getExtent(); + var maxTargetExtent = targetProj.getExtent(); + + var limitedTargetExtent = maxTargetExtent ? + ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent; + + var targetCenter = ol.extent.getCenter(limitedTargetExtent); + var sourceResolution = ol.reproj.calculateSourceResolution( + sourceProj, targetProj, targetCenter, targetResolution); + + var errorThresholdInPixels = ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD; + + /** + * @private + * @type {!ol.reproj.Triangulation} + */ + this.triangulation_ = new ol.reproj.Triangulation( + sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_, + sourceResolution * errorThresholdInPixels); + + /** + * @private + * @type {number} + */ + this.targetResolution_ = targetResolution; + + /** + * @private + * @type {ol.Extent} + */ + this.targetExtent_ = targetExtent; + + var sourceExtent = this.triangulation_.calculateSourceExtent(); + + /** + * @private + * @type {ol.ImageBase} + */ + this.sourceImage_ = + getImageFunction(sourceExtent, sourceResolution, pixelRatio); + + /** + * @private + * @type {number} + */ + this.sourcePixelRatio_ = + this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; + + /** + * @private + * @type {?ol.EventsKey} + */ + this.sourceListenerKey_ = null; + + + var state = ol.ImageState.LOADED; + var attributions = []; + + if (this.sourceImage_) { + state = ol.ImageState.IDLE; + attributions = this.sourceImage_.getAttributions(); + } + + ol.ImageBase.call(this, targetExtent, targetResolution, this.sourcePixelRatio_, + state, attributions); +}; +ol.inherits(ol.reproj.Image, ol.ImageBase); + + +/** + * @inheritDoc + */ +ol.reproj.Image.prototype.disposeInternal = function() { + if (this.state == ol.ImageState.LOADING) { + this.unlistenSource_(); + } + ol.ImageBase.prototype.disposeInternal.call(this); +}; + + +/** + * @inheritDoc + */ +ol.reproj.Image.prototype.getImage = function(opt_context) { + return this.canvas_; +}; + + +/** + * @return {ol.proj.Projection} Projection. + */ +ol.reproj.Image.prototype.getProjection = function() { + return this.targetProj_; +}; + + +/** + * @private + */ +ol.reproj.Image.prototype.reproject_ = function() { + var sourceState = this.sourceImage_.getState(); + if (sourceState == ol.ImageState.LOADED) { + var width = ol.extent.getWidth(this.targetExtent_) / this.targetResolution_; + var height = + ol.extent.getHeight(this.targetExtent_) / this.targetResolution_; + + this.canvas_ = ol.reproj.render(width, height, this.sourcePixelRatio_, + this.sourceImage_.getResolution(), this.maxSourceExtent_, + this.targetResolution_, this.targetExtent_, this.triangulation_, [{ + extent: this.sourceImage_.getExtent(), + image: this.sourceImage_.getImage() + }], 0); + } + this.state = sourceState; + this.changed(); +}; + + +/** + * @inheritDoc + */ +ol.reproj.Image.prototype.load = function() { + if (this.state == ol.ImageState.IDLE) { + this.state = ol.ImageState.LOADING; + this.changed(); + + var sourceState = this.sourceImage_.getState(); + if (sourceState == ol.ImageState.LOADED || + sourceState == ol.ImageState.ERROR) { + this.reproject_(); + } else { + this.sourceListenerKey_ = ol.events.listen(this.sourceImage_, + ol.events.EventType.CHANGE, function(e) { + var sourceState = this.sourceImage_.getState(); + if (sourceState == ol.ImageState.LOADED || + sourceState == ol.ImageState.ERROR) { + this.unlistenSource_(); + this.reproject_(); + } + }, this); + this.sourceImage_.load(); + } + } +}; + + +/** + * @private + */ +ol.reproj.Image.prototype.unlistenSource_ = function() { + goog.asserts.assert(this.sourceListenerKey_, + 'this.sourceListenerKey_ should not be null'); + ol.events.unlistenByKey(this.sourceListenerKey_); + this.sourceListenerKey_ = null; +}; + +goog.provide('ol.source.Image'); +goog.provide('ol.source.ImageEvent'); + +goog.require('goog.asserts'); +goog.require('ol.events.Event'); +goog.require('ol.ImageState'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.proj'); +goog.require('ol.reproj.Image'); +goog.require('ol.source.Source'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for sources providing a single image. + * + * @constructor + * @extends {ol.source.Source} + * @param {ol.SourceImageOptions} options Single image source options. + * @api + */ +ol.source.Image = function(options) { + + ol.source.Source.call(this, { + attributions: options.attributions, + extent: options.extent, + logo: options.logo, + projection: options.projection, + state: options.state + }); + + /** + * @private + * @type {Array.<number>} + */ + this.resolutions_ = options.resolutions !== undefined ? + options.resolutions : null; + goog.asserts.assert(!this.resolutions_ || + ol.array.isSorted(this.resolutions_, + function(a, b) { + return b - a; + }, true), 'resolutions must be null or sorted in descending order'); + + + /** + * @private + * @type {ol.reproj.Image} + */ + this.reprojectedImage_ = null; + + + /** + * @private + * @type {number} + */ + this.reprojectedRevision_ = 0; + +}; +ol.inherits(ol.source.Image, ol.source.Source); + + +/** + * @return {Array.<number>} Resolutions. + */ +ol.source.Image.prototype.getResolutions = function() { + return this.resolutions_; +}; + + +/** + * @protected + * @param {number} resolution Resolution. + * @return {number} Resolution. + */ +ol.source.Image.prototype.findNearestResolution = function(resolution) { + if (this.resolutions_) { + var idx = ol.array.linearFindNearest(this.resolutions_, resolution, 0); + resolution = this.resolutions_[idx]; + } + return resolution; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {ol.ImageBase} Single image. + */ +ol.source.Image.prototype.getImage = function(extent, resolution, pixelRatio, projection) { + var sourceProjection = this.getProjection(); + if (!ol.ENABLE_RASTER_REPROJECTION || + !sourceProjection || + !projection || + ol.proj.equivalent(sourceProjection, projection)) { + if (sourceProjection) { + projection = sourceProjection; + } + return this.getImageInternal(extent, resolution, pixelRatio, projection); + } else { + if (this.reprojectedImage_) { + if (this.reprojectedRevision_ == this.getRevision() && + ol.proj.equivalent( + this.reprojectedImage_.getProjection(), projection) && + this.reprojectedImage_.getResolution() == resolution && + this.reprojectedImage_.getPixelRatio() == pixelRatio && + ol.extent.equals(this.reprojectedImage_.getExtent(), extent)) { + return this.reprojectedImage_; + } + this.reprojectedImage_.dispose(); + this.reprojectedImage_ = null; + } + + this.reprojectedImage_ = new ol.reproj.Image( + sourceProjection, projection, extent, resolution, pixelRatio, + function(extent, resolution, pixelRatio) { + return this.getImageInternal(extent, resolution, + pixelRatio, sourceProjection); + }.bind(this)); + this.reprojectedRevision_ = this.getRevision(); + + return this.reprojectedImage_; + } +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {ol.ImageBase} Single image. + * @protected + */ +ol.source.Image.prototype.getImageInternal = goog.abstractMethod; + + +/** + * Handle image change events. + * @param {ol.events.Event} event Event. + * @protected + */ +ol.source.Image.prototype.handleImageChange = function(event) { + var image = /** @type {ol.Image} */ (event.target); + switch (image.getState()) { + case ol.ImageState.LOADING: + this.dispatchEvent( + new ol.source.ImageEvent(ol.source.ImageEventType.IMAGELOADSTART, + image)); + break; + case ol.ImageState.LOADED: + this.dispatchEvent( + new ol.source.ImageEvent(ol.source.ImageEventType.IMAGELOADEND, + image)); + break; + case ol.ImageState.ERROR: + this.dispatchEvent( + new ol.source.ImageEvent(ol.source.ImageEventType.IMAGELOADERROR, + image)); + break; + default: + // pass + } +}; + + +/** + * Default image load function for image sources that use ol.Image image + * instances. + * @param {ol.Image} image Image. + * @param {string} src Source. + */ +ol.source.Image.defaultImageLoadFunction = function(image, src) { + image.getImage().src = src; +}; + + +/** + * @classdesc + * Events emitted by {@link ol.source.Image} instances are instances of this + * type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.source.ImageEvent} + * @param {string} type Type. + * @param {ol.Image} image The image. + */ +ol.source.ImageEvent = function(type, image) { + + ol.events.Event.call(this, type); + + /** + * The image related to the event. + * @type {ol.Image} + * @api + */ + this.image = image; + +}; +ol.inherits(ol.source.ImageEvent, ol.events.Event); + + +/** + * @enum {string} + */ +ol.source.ImageEventType = { + + /** + * Triggered when an image starts loading. + * @event ol.source.ImageEvent#imageloadstart + * @api + */ + IMAGELOADSTART: 'imageloadstart', + + /** + * Triggered when an image finishes loading. + * @event ol.source.ImageEvent#imageloadend + * @api + */ + IMAGELOADEND: 'imageloadend', + + /** + * Triggered if image loading results in an error. + * @event ol.source.ImageEvent#imageloaderror + * @api + */ + IMAGELOADERROR: 'imageloaderror' + +}; + +goog.provide('ol.source.ImageCanvas'); + +goog.require('ol.ImageCanvas'); +goog.require('ol.extent'); +goog.require('ol.source.Image'); + + +/** + * @classdesc + * Base class for image sources where a canvas element is the image. + * + * @constructor + * @extends {ol.source.Image} + * @param {olx.source.ImageCanvasOptions} options Constructor options. + * @api + */ +ol.source.ImageCanvas = function(options) { + + ol.source.Image.call(this, { + attributions: options.attributions, + logo: options.logo, + projection: options.projection, + resolutions: options.resolutions, + state: options.state + }); + + /** + * @private + * @type {ol.CanvasFunctionType} + */ + this.canvasFunction_ = options.canvasFunction; + + /** + * @private + * @type {ol.ImageCanvas} + */ + this.canvas_ = null; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? + options.ratio : 1.5; + +}; +ol.inherits(ol.source.ImageCanvas, ol.source.Image); + + +/** + * @inheritDoc + */ +ol.source.ImageCanvas.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { + resolution = this.findNearestResolution(resolution); + + var canvas = this.canvas_; + if (canvas && + this.renderedRevision_ == this.getRevision() && + canvas.getResolution() == resolution && + canvas.getPixelRatio() == pixelRatio && + ol.extent.containsExtent(canvas.getExtent(), extent)) { + return canvas; + } + + extent = extent.slice(); + ol.extent.scaleFromCenter(extent, this.ratio_); + var width = ol.extent.getWidth(extent) / resolution; + var height = ol.extent.getHeight(extent) / resolution; + var size = [width * pixelRatio, height * pixelRatio]; + + var canvasElement = this.canvasFunction_( + extent, resolution, pixelRatio, size, projection); + if (canvasElement) { + canvas = new ol.ImageCanvas(extent, resolution, pixelRatio, + this.getAttributions(), canvasElement); + } + this.canvas_ = canvas; + this.renderedRevision_ = this.getRevision(); + + return canvas; +}; + +goog.provide('ol.Feature'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol'); +goog.require('ol.Object'); +goog.require('ol.geom.Geometry'); +goog.require('ol.style.Style'); + + +/** + * @classdesc + * A vector object for geographic features with a geometry and other + * attribute properties, similar to the features in vector file formats like + * GeoJSON. + * + * Features can be styled individually with `setStyle`; otherwise they use the + * style of their vector layer. + * + * Note that attribute properties are set as {@link ol.Object} properties on + * the feature object, so they are observable, and have get/set accessors. + * + * Typically, a feature has a single geometry property. You can set the + * geometry using the `setGeometry` method and get it with `getGeometry`. + * It is possible to store more than one geometry on a feature using attribute + * properties. By default, the geometry used for rendering is identified by + * the property name `geometry`. If you want to use another geometry property + * for rendering, use the `setGeometryName` method to change the attribute + * property associated with the geometry for the feature. For example: + * + * ```js + * var feature = new ol.Feature({ + * geometry: new ol.geom.Polygon(polyCoords), + * labelPoint: new ol.geom.Point(labelCoords), + * name: 'My Polygon' + * }); + * + * // get the polygon geometry + * var poly = feature.getGeometry(); + * + * // Render the feature as a point using the coordinates from labelPoint + * feature.setGeometryName('labelPoint'); + * + * // get the point geometry + * var point = feature.getGeometry(); + * ``` + * + * @constructor + * @extends {ol.Object} + * @param {ol.geom.Geometry|Object.<string, *>=} opt_geometryOrProperties + * You may pass a Geometry object directly, or an object literal + * containing properties. If you pass an object literal, you may + * include a Geometry associated with a `geometry` key. + * @api stable + */ +ol.Feature = function(opt_geometryOrProperties) { + + ol.Object.call(this); + + /** + * @private + * @type {number|string|undefined} + */ + this.id_ = undefined; + + /** + * @type {string} + * @private + */ + this.geometryName_ = 'geometry'; + + /** + * User provided style. + * @private + * @type {ol.style.Style|Array.<ol.style.Style>| + * ol.FeatureStyleFunction} + */ + this.style_ = null; + + /** + * @private + * @type {ol.FeatureStyleFunction|undefined} + */ + this.styleFunction_ = undefined; + + /** + * @private + * @type {?ol.EventsKey} + */ + this.geometryChangeKey_ = null; + + ol.events.listen( + this, ol.Object.getChangeEventType(this.geometryName_), + this.handleGeometryChanged_, this); + + if (opt_geometryOrProperties !== undefined) { + if (opt_geometryOrProperties instanceof ol.geom.Geometry || + !opt_geometryOrProperties) { + var geometry = opt_geometryOrProperties; + this.setGeometry(geometry); + } else { + goog.asserts.assert(goog.isObject(opt_geometryOrProperties), + 'opt_geometryOrProperties should be an Object'); + /** @type {Object.<string, *>} */ + var properties = opt_geometryOrProperties; + this.setProperties(properties); + } + } +}; +ol.inherits(ol.Feature, ol.Object); + + +/** + * Clone this feature. If the original feature has a geometry it + * is also cloned. The feature id is not set in the clone. + * @return {ol.Feature} The clone. + * @api stable + */ +ol.Feature.prototype.clone = function() { + var clone = new ol.Feature(this.getProperties()); + clone.setGeometryName(this.getGeometryName()); + var geometry = this.getGeometry(); + if (geometry) { + clone.setGeometry(geometry.clone()); + } + var style = this.getStyle(); + if (style) { + clone.setStyle(style); + } + return clone; +}; + + +/** + * Get the feature's default geometry. A feature may have any number of named + * geometries. The "default" geometry (the one that is rendered by default) is + * set when calling {@link ol.Feature#setGeometry}. + * @return {ol.geom.Geometry|undefined} The default geometry for the feature. + * @api stable + * @observable + */ +ol.Feature.prototype.getGeometry = function() { + return /** @type {ol.geom.Geometry|undefined} */ ( + this.get(this.geometryName_)); +}; + + +/** + * Get the feature identifier. This is a stable identifier for the feature and + * is either set when reading data from a remote source or set explicitly by + * calling {@link ol.Feature#setId}. + * @return {number|string|undefined} Id. + * @api stable + * @observable + */ +ol.Feature.prototype.getId = function() { + return this.id_; +}; + + +/** + * Get the name of the feature's default geometry. By default, the default + * geometry is named `geometry`. + * @return {string} Get the property name associated with the default geometry + * for this feature. + * @api stable + */ +ol.Feature.prototype.getGeometryName = function() { + return this.geometryName_; +}; + + +/** + * Get the feature's style. This return for this method depends on what was + * provided to the {@link ol.Feature#setStyle} method. + * @return {ol.style.Style|Array.<ol.style.Style>| + * ol.FeatureStyleFunction} The feature style. + * @api stable + * @observable + */ +ol.Feature.prototype.getStyle = function() { + return this.style_; +}; + + +/** + * Get the feature's style function. + * @return {ol.FeatureStyleFunction|undefined} Return a function + * representing the current style of this feature. + * @api stable + */ +ol.Feature.prototype.getStyleFunction = function() { + return this.styleFunction_; +}; + + +/** + * @private + */ +ol.Feature.prototype.handleGeometryChange_ = function() { + this.changed(); +}; + + +/** + * @private + */ +ol.Feature.prototype.handleGeometryChanged_ = function() { + if (this.geometryChangeKey_) { + ol.events.unlistenByKey(this.geometryChangeKey_); + this.geometryChangeKey_ = null; + } + var geometry = this.getGeometry(); + if (geometry) { + this.geometryChangeKey_ = ol.events.listen(geometry, + ol.events.EventType.CHANGE, this.handleGeometryChange_, this); + } + this.changed(); +}; + + +/** + * Set the default geometry for the feature. This will update the property + * with the name returned by {@link ol.Feature#getGeometryName}. + * @param {ol.geom.Geometry|undefined} geometry The new geometry. + * @api stable + * @observable + */ +ol.Feature.prototype.setGeometry = function(geometry) { + this.set(this.geometryName_, geometry); +}; + + +/** + * Set the style for the feature. This can be a single style object, an array + * of styles, or a function that takes a resolution and returns an array of + * styles. If it is `null` the feature has no style (a `null` style). + * @param {ol.style.Style|Array.<ol.style.Style>| + * ol.FeatureStyleFunction} style Style for this feature. + * @api stable + * @observable + */ +ol.Feature.prototype.setStyle = function(style) { + this.style_ = style; + this.styleFunction_ = !style ? + undefined : ol.Feature.createStyleFunction(style); + this.changed(); +}; + + +/** + * Set the feature id. The feature id is considered stable and may be used when + * requesting features or comparing identifiers returned from a remote source. + * The feature id can be used with the {@link ol.source.Vector#getFeatureById} + * method. + * @param {number|string|undefined} id The feature id. + * @api stable + * @observable + */ +ol.Feature.prototype.setId = function(id) { + this.id_ = id; + this.changed(); +}; + + +/** + * Set the property name to be used when getting the feature's default geometry. + * When calling {@link ol.Feature#getGeometry}, the value of the property with + * this name will be returned. + * @param {string} name The property name of the default geometry. + * @api stable + */ +ol.Feature.prototype.setGeometryName = function(name) { + ol.events.unlisten( + this, ol.Object.getChangeEventType(this.geometryName_), + this.handleGeometryChanged_, this); + this.geometryName_ = name; + ol.events.listen( + this, ol.Object.getChangeEventType(this.geometryName_), + this.handleGeometryChanged_, this); + this.handleGeometryChanged_(); +}; + + +/** + * Convert the provided object into a feature style function. Functions passed + * through unchanged. Arrays of ol.style.Style or single style objects wrapped + * in a new feature style function. + * @param {ol.FeatureStyleFunction|!Array.<ol.style.Style>|!ol.style.Style} obj + * A feature style function, a single style, or an array of styles. + * @return {ol.FeatureStyleFunction} A style function. + */ +ol.Feature.createStyleFunction = function(obj) { + var styleFunction; + + if (typeof obj === 'function') { + styleFunction = obj; + } else { + /** + * @type {Array.<ol.style.Style>} + */ + var styles; + if (Array.isArray(obj)) { + styles = obj; + } else { + goog.asserts.assertInstanceof(obj, ol.style.Style, + 'obj should be an ol.style.Style'); + styles = [obj]; + } + styleFunction = function() { + return styles; + }; + } + return styleFunction; +}; + +goog.provide('ol.VectorTile'); + +goog.require('ol.Tile'); +goog.require('ol.TileState'); +goog.require('ol.dom'); +goog.require('ol.proj.Projection'); + + +/** + * @constructor + * @extends {ol.Tile} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.TileState} state State. + * @param {string} src Data source url. + * @param {ol.format.Feature} format Feature format. + * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. + */ +ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) { + + ol.Tile.call(this, tileCoord, state); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = ol.dom.createCanvasContext2D(); + + /** + * @private + * @type {ol.format.Feature} + */ + this.format_ = format; + + /** + * @private + * @type {Array.<ol.Feature>} + */ + this.features_ = null; + + /** + * @private + * @type {ol.FeatureLoader} + */ + this.loader_; + + /** + * Data projection + * @private + * @type {ol.proj.Projection} + */ + this.projection_; + + /** + * @private + * @type {ol.TileReplayState} + */ + this.replayState_ = { + dirty: false, + renderedRenderOrder: null, + renderedRevision: -1, + renderedTileRevision: -1, + replayGroup: null, + skippedFeatures: [] + }; + + /** + * @private + * @type {ol.TileLoadFunctionType} + */ + this.tileLoadFunction_ = tileLoadFunction; + + /** + * @private + * @type {string} + */ + this.url_ = src; + +}; +ol.inherits(ol.VectorTile, ol.Tile); + + +/** + * @return {CanvasRenderingContext2D} The rendering context. + */ +ol.VectorTile.prototype.getContext = function() { + return this.context_; +}; + + +/** + * @inheritDoc + */ +ol.VectorTile.prototype.getImage = function() { + return this.replayState_.renderedTileRevision == -1 ? + null : this.context_.canvas; +}; + + +/** + * Get the feature format assigned for reading this tile's features. + * @return {ol.format.Feature} Feature format. + * @api + */ +ol.VectorTile.prototype.getFormat = function() { + return this.format_; +}; + + +/** + * @return {Array.<ol.Feature>} Features. + */ +ol.VectorTile.prototype.getFeatures = function() { + return this.features_; +}; + + +/** + * @return {ol.TileReplayState} The replay state. + */ +ol.VectorTile.prototype.getReplayState = function() { + return this.replayState_; +}; + + +/** + * @inheritDoc + */ +ol.VectorTile.prototype.getKey = function() { + return this.url_; +}; + + +/** + * @return {ol.proj.Projection} Feature projection. + */ +ol.VectorTile.prototype.getProjection = function() { + return this.projection_; +}; + + +/** + * Load the tile. + */ +ol.VectorTile.prototype.load = function() { + if (this.state == ol.TileState.IDLE) { + this.setState(ol.TileState.LOADING); + this.tileLoadFunction_(this, this.url_); + this.loader_(null, NaN, null); + } +}; + + +/** + * @param {Array.<ol.Feature>} features Features. + * @api + */ +ol.VectorTile.prototype.setFeatures = function(features) { + this.features_ = features; + this.setState(ol.TileState.LOADED); +}; + + +/** + * Set the projection of the features that were added with {@link #setFeatures}. + * @param {ol.proj.Projection} projection Feature projection. + * @api + */ +ol.VectorTile.prototype.setProjection = function(projection) { + this.projection_ = projection; +}; + + +/** + * @param {ol.TileState} tileState Tile state. + */ +ol.VectorTile.prototype.setState = function(tileState) { + this.state = tileState; + this.changed(); +}; + + +/** + * Set the feature loader for reading this tile's features. + * @param {ol.FeatureLoader} loader Feature loader. + * @api + */ +ol.VectorTile.prototype.setLoader = function(loader) { + this.loader_ = loader; +}; + +goog.provide('ol.format.FormatType'); + + +/** + * @enum {string} + */ +ol.format.FormatType = { + ARRAY_BUFFER: 'arraybuffer', + JSON: 'json', + TEXT: 'text', + XML: 'xml' +}; + +goog.provide('ol.xml'); + +goog.require('goog.asserts'); +goog.require('ol.array'); + + +/** + * This document should be used when creating nodes for XML serializations. This + * document is also used by {@link ol.xml.createElementNS} and + * {@link ol.xml.setAttributeNS} + * @const + * @type {Document} + */ +ol.xml.DOCUMENT = document.implementation.createDocument('', '', null); + + +/** + * @param {string} namespaceURI Namespace URI. + * @param {string} qualifiedName Qualified name. + * @return {Node} Node. + */ +ol.xml.createElementNS = function(namespaceURI, qualifiedName) { + return ol.xml.DOCUMENT.createElementNS(namespaceURI, qualifiedName); +}; + + +/** + * Recursively grab all text content of child nodes into a single string. + * @param {Node} node Node. + * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line + * breaks. + * @return {string} All text content. + * @api + */ +ol.xml.getAllTextContent = function(node, normalizeWhitespace) { + return ol.xml.getAllTextContent_(node, normalizeWhitespace, []).join(''); +}; + + +/** + * Recursively grab all text content of child nodes into a single string. + * @param {Node} node Node. + * @param {boolean} normalizeWhitespace Normalize whitespace: remove all line + * breaks. + * @param {Array.<string>} accumulator Accumulator. + * @private + * @return {Array.<string>} Accumulator. + */ +ol.xml.getAllTextContent_ = function(node, normalizeWhitespace, accumulator) { + if (node.nodeType == Node.CDATA_SECTION_NODE || + node.nodeType == Node.TEXT_NODE) { + if (normalizeWhitespace) { + accumulator.push(String(node.nodeValue).replace(/(\r\n|\r|\n)/g, '')); + } else { + accumulator.push(node.nodeValue); + } + } else { + var n; + for (n = node.firstChild; n; n = n.nextSibling) { + ol.xml.getAllTextContent_(n, normalizeWhitespace, accumulator); + } + } + return accumulator; +}; + + +/** + * @param {?} value Value. + * @return {boolean} Is document. + */ +ol.xml.isDocument = function(value) { + return value instanceof Document; +}; + + +/** + * @param {?} value Value. + * @return {boolean} Is node. + */ +ol.xml.isNode = function(value) { + return value instanceof Node; +}; + + +/** + * @param {Node} node Node. + * @param {?string} namespaceURI Namespace URI. + * @param {string} name Attribute name. + * @return {string} Value + */ +ol.xml.getAttributeNS = function(node, namespaceURI, name) { + return node.getAttributeNS(namespaceURI, name) || ''; +}; + + +/** + * @param {Node} node Node. + * @param {?string} namespaceURI Namespace URI. + * @param {string} name Attribute name. + * @param {string|number} value Value. + */ +ol.xml.setAttributeNS = function(node, namespaceURI, name, value) { + node.setAttributeNS(namespaceURI, name, value); +}; + + +/** + * Parse an XML string to an XML Document. + * @param {string} xml XML. + * @return {Document} Document. + * @api + */ +ol.xml.parse = function(xml) { + return new DOMParser().parseFromString(xml, 'application/xml'); +}; + + +/** + * Make an array extender function for extending the array at the top of the + * object stack. + * @param {function(this: T, Node, Array.<*>): (Array.<*>|undefined)} + * valueReader Value reader. + * @param {T=} opt_this The object to use as `this` in `valueReader`. + * @return {ol.XmlParser} Parser. + * @template T + */ +ol.xml.makeArrayExtender = function(valueReader, opt_this) { + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_this, node, objectStack); + if (value !== undefined) { + goog.asserts.assert(Array.isArray(value), + 'valueReader function is expected to return an array of values'); + var array = /** @type {Array.<*>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(Array.isArray(array), + 'objectStack is supposed to be an array of arrays'); + ol.array.extend(array, value); + } + }); +}; + + +/** + * Make an array pusher function for pushing to the array at the top of the + * object stack. + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. + * @param {T=} opt_this The object to use as `this` in `valueReader`. + * @return {ol.XmlParser} Parser. + * @template T + */ +ol.xml.makeArrayPusher = function(valueReader, opt_this) { + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_this !== undefined ? opt_this : this, + node, objectStack); + if (value !== undefined) { + var array = objectStack[objectStack.length - 1]; + goog.asserts.assert(Array.isArray(array), + 'objectStack is supposed to be an array of arrays'); + array.push(value); + } + }); +}; + + +/** + * Make an object stack replacer function for replacing the object at the + * top of the stack. + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. + * @param {T=} opt_this The object to use as `this` in `valueReader`. + * @return {ol.XmlParser} Parser. + * @template T + */ +ol.xml.makeReplacer = function(valueReader, opt_this) { + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_this !== undefined ? opt_this : this, + node, objectStack); + if (value !== undefined) { + objectStack[objectStack.length - 1] = value; + } + }); +}; + + +/** + * Make an object property pusher function for adding a property to the + * object at the top of the stack. + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. + * @param {string=} opt_property Property. + * @param {T=} opt_this The object to use as `this` in `valueReader`. + * @return {ol.XmlParser} Parser. + * @template T + */ +ol.xml.makeObjectPropertyPusher = function(valueReader, opt_property, opt_this) { + goog.asserts.assert(valueReader !== undefined, + 'undefined valueReader, expected function(this: T, Node, Array.<*>)'); + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_this !== undefined ? opt_this : this, + node, objectStack); + if (value !== undefined) { + var object = /** @type {Object} */ + (objectStack[objectStack.length - 1]); + var property = opt_property !== undefined ? + opt_property : node.localName; + goog.asserts.assert(goog.isObject(object), + 'entity from stack was not an object'); + var array; + if (property in object) { + array = object[property]; + } else { + array = object[property] = []; + } + array.push(value); + } + }); +}; + + +/** + * Make an object property setter function. + * @param {function(this: T, Node, Array.<*>): *} valueReader Value reader. + * @param {string=} opt_property Property. + * @param {T=} opt_this The object to use as `this` in `valueReader`. + * @return {ol.XmlParser} Parser. + * @template T + */ +ol.xml.makeObjectPropertySetter = function(valueReader, opt_property, opt_this) { + goog.asserts.assert(valueReader !== undefined, + 'undefined valueReader, expected function(this: T, Node, Array.<*>)'); + return ( + /** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + */ + function(node, objectStack) { + var value = valueReader.call(opt_this !== undefined ? opt_this : this, + node, objectStack); + if (value !== undefined) { + var object = /** @type {Object} */ + (objectStack[objectStack.length - 1]); + var property = opt_property !== undefined ? + opt_property : node.localName; + goog.asserts.assert(goog.isObject(object), + 'entity from stack was not an object'); + object[property] = value; + } + }); +}; + + +/** + * Create a serializer that appends nodes written by its `nodeWriter` to its + * designated parent. The parent is the `node` of the + * {@link ol.XmlNodeStackItem} at the top of the `objectStack`. + * @param {function(this: T, Node, V, Array.<*>)} + * nodeWriter Node writer. + * @param {T=} opt_this The object to use as `this` in `nodeWriter`. + * @return {ol.XmlSerializer} Serializer. + * @template T, V + */ +ol.xml.makeChildAppender = function(nodeWriter, opt_this) { + return function(node, value, objectStack) { + nodeWriter.call(opt_this !== undefined ? opt_this : this, + node, value, objectStack); + var parent = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(parent), + 'entity from stack was not an object'); + var parentNode = parent.node; + goog.asserts.assert(ol.xml.isNode(parentNode) || + ol.xml.isDocument(parentNode), + 'expected parentNode %s to be a Node or a Document', parentNode); + parentNode.appendChild(node); + }; +}; + + +/** + * Create a serializer that calls the provided `nodeWriter` from + * {@link ol.xml.serialize}. This can be used by the parent writer to have the + * 'nodeWriter' called with an array of values when the `nodeWriter` was + * designed to serialize a single item. An example would be a LineString + * geometry writer, which could be reused for writing MultiLineString + * geometries. + * @param {function(this: T, Node, V, Array.<*>)} + * nodeWriter Node writer. + * @param {T=} opt_this The object to use as `this` in `nodeWriter`. + * @return {ol.XmlSerializer} Serializer. + * @template T, V + */ +ol.xml.makeArraySerializer = function(nodeWriter, opt_this) { + var serializersNS, nodeFactory; + return function(node, value, objectStack) { + if (serializersNS === undefined) { + serializersNS = {}; + var serializers = {}; + serializers[node.localName] = nodeWriter; + serializersNS[node.namespaceURI] = serializers; + nodeFactory = ol.xml.makeSimpleNodeFactory(node.localName); + } + ol.xml.serialize(serializersNS, nodeFactory, value, objectStack); + }; +}; + + +/** + * Create a node factory which can use the `opt_keys` passed to + * {@link ol.xml.serialize} or {@link ol.xml.pushSerializeAndPop} as node names, + * or a fixed node name. The namespace of the created nodes can either be fixed, + * or the parent namespace will be used. + * @param {string=} opt_nodeName Fixed node name which will be used for all + * created nodes. If not provided, the 3rd argument to the resulting node + * factory needs to be provided and will be the nodeName. + * @param {string=} opt_namespaceURI Fixed namespace URI which will be used for + * all created nodes. If not provided, the namespace of the parent node will + * be used. + * @return {function(*, Array.<*>, string=): (Node|undefined)} Node factory. + */ +ol.xml.makeSimpleNodeFactory = function(opt_nodeName, opt_namespaceURI) { + var fixedNodeName = opt_nodeName; + return ( + /** + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node} Node. + */ + function(value, objectStack, opt_nodeName) { + var context = objectStack[objectStack.length - 1]; + var node = context.node; + goog.asserts.assert(ol.xml.isNode(node) || ol.xml.isDocument(node), + 'expected node %s to be a Node or a Document', node); + var nodeName = fixedNodeName; + if (nodeName === undefined) { + nodeName = opt_nodeName; + } + var namespaceURI = opt_namespaceURI; + if (opt_namespaceURI === undefined) { + namespaceURI = node.namespaceURI; + } + goog.asserts.assert(nodeName !== undefined, 'nodeName was undefined'); + return ol.xml.createElementNS(namespaceURI, nodeName); + } + ); +}; + + +/** + * A node factory that creates a node using the parent's `namespaceURI` and the + * `nodeName` passed by {@link ol.xml.serialize} or + * {@link ol.xml.pushSerializeAndPop} to the node factory. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + */ +ol.xml.OBJECT_PROPERTY_NODE_FACTORY = ol.xml.makeSimpleNodeFactory(); + + +/** + * Create an array of `values` to be used with {@link ol.xml.serialize} or + * {@link ol.xml.pushSerializeAndPop}, where `orderedKeys` has to be provided as + * `opt_key` argument. + * @param {Object.<string, V>} object Key-value pairs for the sequence. Keys can + * be a subset of the `orderedKeys`. + * @param {Array.<string>} orderedKeys Keys in the order of the sequence. + * @return {Array.<V>} Values in the order of the sequence. The resulting array + * has the same length as the `orderedKeys` array. Values that are not + * present in `object` will be `undefined` in the resulting array. + * @template V + */ +ol.xml.makeSequence = function(object, orderedKeys) { + var length = orderedKeys.length; + var sequence = new Array(length); + for (var i = 0; i < length; ++i) { + sequence[i] = object[orderedKeys[i]]; + } + return sequence; +}; + + +/** + * Create a namespaced structure, using the same values for each namespace. + * This can be used as a starting point for versioned parsers, when only a few + * values are version specific. + * @param {Array.<string>} namespaceURIs Namespace URIs. + * @param {T} structure Structure. + * @param {Object.<string, T>=} opt_structureNS Namespaced structure to add to. + * @return {Object.<string, T>} Namespaced structure. + * @template T + */ +ol.xml.makeStructureNS = function(namespaceURIs, structure, opt_structureNS) { + /** + * @type {Object.<string, *>} + */ + var structureNS = opt_structureNS !== undefined ? opt_structureNS : {}; + var i, ii; + for (i = 0, ii = namespaceURIs.length; i < ii; ++i) { + structureNS[namespaceURIs[i]] = structure; + } + return structureNS; +}; + + +/** + * Parse a node using the parsers and object stack. + * @param {Object.<string, Object.<string, ol.XmlParser>>} parsersNS + * Parsers by namespace. + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @param {*=} opt_this The object to use as `this`. + */ +ol.xml.parseNode = function(parsersNS, node, objectStack, opt_this) { + var n; + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + var parsers = parsersNS[n.namespaceURI]; + if (parsers !== undefined) { + var parser = parsers[n.localName]; + if (parser !== undefined) { + parser.call(opt_this, n, objectStack); + } + } + } +}; + + +/** + * Push an object on top of the stack, parse and return the popped object. + * @param {T} object Object. + * @param {Object.<string, Object.<string, ol.XmlParser>>} parsersNS + * Parsers by namespace. + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @param {*=} opt_this The object to use as `this`. + * @return {T} Object. + * @template T + */ +ol.xml.pushParseAndPop = function( + object, parsersNS, node, objectStack, opt_this) { + objectStack.push(object); + ol.xml.parseNode(parsersNS, node, objectStack, opt_this); + return objectStack.pop(); +}; + + +/** + * Walk through an array of `values` and call a serializer for each value. + * @param {Object.<string, Object.<string, ol.XmlSerializer>>} serializersNS + * Namespaced serializers. + * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory + * Node factory. The `nodeFactory` creates the node whose namespace and name + * will be used to choose a node writer from `serializersNS`. This + * separation allows us to decide what kind of node to create, depending on + * the value we want to serialize. An example for this would be different + * geometry writers based on the geometry type. + * @param {Array.<*>} values Values to serialize. An example would be an array + * of {@link ol.Feature} instances. + * @param {Array.<*>} objectStack Node stack. + * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the + * `nodeFactory`. This is used for serializing object literals where the + * node name relates to the property key. The array length of `opt_keys` has + * to match the length of `values`. For serializing a sequence, `opt_keys` + * determines the order of the sequence. + * @param {T=} opt_this The object to use as `this` for the node factory and + * serializers. + * @template T + */ +ol.xml.serialize = function( + serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) { + var length = (opt_keys !== undefined ? opt_keys : values).length; + var value, node; + for (var i = 0; i < length; ++i) { + value = values[i]; + if (value !== undefined) { + node = nodeFactory.call(opt_this, value, objectStack, + opt_keys !== undefined ? opt_keys[i] : undefined); + if (node !== undefined) { + serializersNS[node.namespaceURI][node.localName] + .call(opt_this, node, value, objectStack); + } + } + } +}; + + +/** + * @param {O} object Object. + * @param {Object.<string, Object.<string, ol.XmlSerializer>>} serializersNS + * Namespaced serializers. + * @param {function(this: T, *, Array.<*>, (string|undefined)): (Node|undefined)} nodeFactory + * Node factory. The `nodeFactory` creates the node whose namespace and name + * will be used to choose a node writer from `serializersNS`. This + * separation allows us to decide what kind of node to create, depending on + * the value we want to serialize. An example for this would be different + * geometry writers based on the geometry type. + * @param {Array.<*>} values Values to serialize. An example would be an array + * of {@link ol.Feature} instances. + * @param {Array.<*>} objectStack Node stack. + * @param {Array.<string>=} opt_keys Keys of the `values`. Will be passed to the + * `nodeFactory`. This is used for serializing object literals where the + * node name relates to the property key. The array length of `opt_keys` has + * to match the length of `values`. For serializing a sequence, `opt_keys` + * determines the order of the sequence. + * @param {T=} opt_this The object to use as `this` for the node factory and + * serializers. + * @return {O|undefined} Object. + * @template O, T + */ +ol.xml.pushSerializeAndPop = function(object, + serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this) { + objectStack.push(object); + ol.xml.serialize( + serializersNS, nodeFactory, values, objectStack, opt_keys, opt_this); + return objectStack.pop(); +}; + +goog.provide('ol.featureloader'); + +goog.require('goog.asserts'); +goog.require('ol.TileState'); +goog.require('ol.VectorTile'); +goog.require('ol.format.FormatType'); +goog.require('ol.proj'); +goog.require('ol.proj.Projection'); +goog.require('ol.xml'); + + +/** + * @param {string|ol.FeatureUrlFunction} url Feature URL service. + * @param {ol.format.Feature} format Feature format. + * @param {function(this:ol.VectorTile, Array.<ol.Feature>, ol.proj.Projection)|function(this:ol.source.Vector, Array.<ol.Feature>)} success + * Function called with the loaded features and optionally with the data + * projection. Called with the vector tile or source as `this`. + * @param {function(this:ol.VectorTile)|function(this:ol.source.Vector)} failure + * Function called when loading failed. Called with the vector tile or + * source as `this`. + * @return {ol.FeatureLoader} The feature loader. + */ +ol.featureloader.loadFeaturesXhr = function(url, format, success, failure) { + return ( + /** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {ol.proj.Projection} projection Projection. + * @this {ol.source.Vector|ol.VectorTile} + */ + function(extent, resolution, projection) { + var xhr = new XMLHttpRequest(); + xhr.open('GET', + typeof url === 'function' ? url(extent, resolution, projection) : url, + true); + if (format.getType() == ol.format.FormatType.ARRAY_BUFFER) { + xhr.responseType = 'arraybuffer'; + } + /** + * @param {Event} event Event. + * @private + */ + xhr.onload = function(event) { + if (xhr.status >= 200 && xhr.status < 300) { + var type = format.getType(); + /** @type {Document|Node|Object|string|undefined} */ + var source; + if (type == ol.format.FormatType.JSON || + type == ol.format.FormatType.TEXT) { + source = xhr.responseText; + } else if (type == ol.format.FormatType.XML) { + source = xhr.responseXML; + if (!source) { + source = ol.xml.parse(xhr.responseText); + } + } else if (type == ol.format.FormatType.ARRAY_BUFFER) { + source = /** @type {ArrayBuffer} */ (xhr.response); + } else { + goog.asserts.fail('unexpected format type'); + } + if (source) { + success.call(this, format.readFeatures(source, + {featureProjection: projection}), + format.readProjection(source)); + } else { + goog.asserts.fail('undefined or null source'); + } + } else { + failure.call(this); + } + }.bind(this); + xhr.send(); + }); +}; + + +/** + * Create an XHR feature loader for a `url` and `format`. The feature loader + * loads features (with XHR), parses the features, and adds them to the + * vector tile. + * @param {string|ol.FeatureUrlFunction} url Feature URL service. + * @param {ol.format.Feature} format Feature format. + * @return {ol.FeatureLoader} The feature loader. + * @api + */ +ol.featureloader.tile = function(url, format) { + return ol.featureloader.loadFeaturesXhr(url, format, + /** + * @param {Array.<ol.Feature>} features The loaded features. + * @param {ol.proj.Projection} dataProjection Data projection. + * @this {ol.VectorTile} + */ + function(features, dataProjection) { + this.setProjection(dataProjection); + this.setFeatures(features); + }, + /** + * @this {ol.VectorTile} + */ + function() { + this.setState(ol.TileState.ERROR); + }); +}; + + +/** + * Create an XHR feature loader for a `url` and `format`. The feature loader + * loads features (with XHR), parses the features, and adds them to the + * vector source. + * @param {string|ol.FeatureUrlFunction} url Feature URL service. + * @param {ol.format.Feature} format Feature format. + * @return {ol.FeatureLoader} The feature loader. + * @api + */ +ol.featureloader.xhr = function(url, format) { + return ol.featureloader.loadFeaturesXhr(url, format, + /** + * @param {Array.<ol.Feature>} features The loaded features. + * @param {ol.proj.Projection} dataProjection Data projection. + * @this {ol.source.Vector} + */ + function(features, dataProjection) { + this.addFeatures(features); + }, /* FIXME handle error */ ol.nullFunction); +}; + +goog.provide('ol.loadingstrategy'); + + +/** + * Strategy function for loading all features with a single request. + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @return {Array.<ol.Extent>} Extents. + * @api + */ +ol.loadingstrategy.all = function(extent, resolution) { + return [[-Infinity, -Infinity, Infinity, Infinity]]; +}; + + +/** + * Strategy function for loading features based on the view's extent and + * resolution. + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @return {Array.<ol.Extent>} Extents. + * @api + */ +ol.loadingstrategy.bbox = function(extent, resolution) { + return [extent]; +}; + + +/** + * Creates a strategy function for loading features based on a tile grid. + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @return {function(ol.Extent, number): Array.<ol.Extent>} Loading strategy. + * @api + */ +ol.loadingstrategy.tile = function(tileGrid) { + return ( + /** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @return {Array.<ol.Extent>} Extents. + */ + function(extent, resolution) { + var z = tileGrid.getZForResolution(resolution); + var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + /** @type {Array.<ol.Extent>} */ + var extents = []; + /** @type {ol.TileCoord} */ + var tileCoord = [z, 0, 0]; + for (tileCoord[1] = tileRange.minX; tileCoord[1] <= tileRange.maxX; + ++tileCoord[1]) { + for (tileCoord[2] = tileRange.minY; tileCoord[2] <= tileRange.maxY; + ++tileCoord[2]) { + extents.push(tileGrid.getTileCoordExtent(tileCoord)); + } + } + return extents; + }); +}; + +goog.provide('ol.ext.rbush'); +/** @typedef {function(*)} */ +ol.ext.rbush; +(function() { +var exports = {}; +var module = {exports: exports}; +var define; +/** + * @fileoverview + * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility} + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.rbush = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = partialSort; + +// Floyd-Rivest selection algorithm: +// Rearrange items so that all items in the [left, k] range are smaller than all items in (k, right]; +// The k-th element will have the (k - left + 1)th smallest value in [left, right] + +function partialSort(arr, k, left, right, compare) { + left = left || 0; + right = right || (arr.length - 1); + compare = compare || defaultCompare; + + while (right > left) { + if (right - left > 600) { + var n = right - left + 1; + var m = k - left + 1; + var z = Math.log(n); + var s = 0.5 * Math.exp(2 * z / 3); + var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1); + var newLeft = Math.max(left, Math.floor(k - m * s / n + sd)); + var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd)); + partialSort(arr, k, newLeft, newRight, compare); + } + + var t = arr[k]; + var i = left; + var j = right; + + swap(arr, left, k); + if (compare(arr[right], t) > 0) swap(arr, left, right); + + while (i < j) { + swap(arr, i, j); + i++; + j--; + while (compare(arr[i], t) < 0) i++; + while (compare(arr[j], t) > 0) j--; + } + + if (compare(arr[left], t) === 0) swap(arr, left, j); + else { + j++; + swap(arr, j, right); + } + + if (j <= k) left = j + 1; + if (k <= j) right = j - 1; + } +} + +function swap(arr, i, j) { + var tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; +} + +function defaultCompare(a, b) { + return a < b ? -1 : a > b ? 1 : 0; +} + +},{}],2:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = rbush; + +var quickselect = _dereq_('quickselect'); + +function rbush(maxEntries, format) { + if (!(this instanceof rbush)) return new rbush(maxEntries, format); + + // max entries in a node is 9 by default; min node fill is 40% for best performance + this._maxEntries = Math.max(4, maxEntries || 9); + this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4)); + + if (format) { + this._initFormat(format); + } + + this.clear(); +} + +rbush.prototype = { + + all: function () { + return this._all(this.data, []); + }, + + search: function (bbox) { + + var node = this.data, + result = [], + toBBox = this.toBBox; + + if (!intersects(bbox, node)) return result; + + var nodesToSearch = [], + i, len, child, childBBox; + + while (node) { + for (i = 0, len = node.children.length; i < len; i++) { + + child = node.children[i]; + childBBox = node.leaf ? toBBox(child) : child; + + if (intersects(bbox, childBBox)) { + if (node.leaf) result.push(child); + else if (contains(bbox, childBBox)) this._all(child, result); + else nodesToSearch.push(child); + } + } + node = nodesToSearch.pop(); + } + + return result; + }, + + collides: function (bbox) { + + var node = this.data, + toBBox = this.toBBox; + + if (!intersects(bbox, node)) return false; + + var nodesToSearch = [], + i, len, child, childBBox; + + while (node) { + for (i = 0, len = node.children.length; i < len; i++) { + + child = node.children[i]; + childBBox = node.leaf ? toBBox(child) : child; + + if (intersects(bbox, childBBox)) { + if (node.leaf || contains(bbox, childBBox)) return true; + nodesToSearch.push(child); + } + } + node = nodesToSearch.pop(); + } + + return false; + }, + + load: function (data) { + if (!(data && data.length)) return this; + + if (data.length < this._minEntries) { + for (var i = 0, len = data.length; i < len; i++) { + this.insert(data[i]); + } + return this; + } + + // recursively build the tree with the given data from stratch using OMT algorithm + var node = this._build(data.slice(), 0, data.length - 1, 0); + + if (!this.data.children.length) { + // save as is if tree is empty + this.data = node; + + } else if (this.data.height === node.height) { + // split root if trees have the same height + this._splitRoot(this.data, node); + + } else { + if (this.data.height < node.height) { + // swap trees if inserted one is bigger + var tmpNode = this.data; + this.data = node; + node = tmpNode; + } + + // insert the small tree into the large tree at appropriate level + this._insert(node, this.data.height - node.height - 1, true); + } + + return this; + }, + + insert: function (item) { + if (item) this._insert(item, this.data.height - 1); + return this; + }, + + clear: function () { + this.data = createNode([]); + return this; + }, + + remove: function (item, equalsFn) { + if (!item) return this; + + var node = this.data, + bbox = this.toBBox(item), + path = [], + indexes = [], + i, parent, index, goingUp; + + // depth-first iterative tree traversal + while (node || path.length) { + + if (!node) { // go up + node = path.pop(); + parent = path[path.length - 1]; + i = indexes.pop(); + goingUp = true; + } + + if (node.leaf) { // check current node + index = findItem(item, node.children, equalsFn); + + if (index !== -1) { + // item found, remove the item and condense tree upwards + node.children.splice(index, 1); + path.push(node); + this._condense(path); + return this; + } + } + + if (!goingUp && !node.leaf && contains(node, bbox)) { // go down + path.push(node); + indexes.push(i); + i = 0; + parent = node; + node = node.children[0]; + + } else if (parent) { // go right + i++; + node = parent.children[i]; + goingUp = false; + + } else node = null; // nothing found + } + + return this; + }, + + toBBox: function (item) { return item; }, + + compareMinX: compareNodeMinX, + compareMinY: compareNodeMinY, + + toJSON: function () { return this.data; }, + + fromJSON: function (data) { + this.data = data; + return this; + }, + + _all: function (node, result) { + var nodesToSearch = []; + while (node) { + if (node.leaf) result.push.apply(result, node.children); + else nodesToSearch.push.apply(nodesToSearch, node.children); + + node = nodesToSearch.pop(); + } + return result; + }, + + _build: function (items, left, right, height) { + + var N = right - left + 1, + M = this._maxEntries, + node; + + if (N <= M) { + // reached leaf level; return leaf + node = createNode(items.slice(left, right + 1)); + calcBBox(node, this.toBBox); + return node; + } + + if (!height) { + // target height of the bulk-loaded tree + height = Math.ceil(Math.log(N) / Math.log(M)); + + // target number of root entries to maximize storage utilization + M = Math.ceil(N / Math.pow(M, height - 1)); + } + + node = createNode([]); + node.leaf = false; + node.height = height; + + // split the items into M mostly square tiles + + var N2 = Math.ceil(N / M), + N1 = N2 * Math.ceil(Math.sqrt(M)), + i, j, right2, right3; + + multiSelect(items, left, right, N1, this.compareMinX); + + for (i = left; i <= right; i += N1) { + + right2 = Math.min(i + N1 - 1, right); + + multiSelect(items, i, right2, N2, this.compareMinY); + + for (j = i; j <= right2; j += N2) { + + right3 = Math.min(j + N2 - 1, right2); + + // pack each entry recursively + node.children.push(this._build(items, j, right3, height - 1)); + } + } + + calcBBox(node, this.toBBox); + + return node; + }, + + _chooseSubtree: function (bbox, node, level, path) { + + var i, len, child, targetNode, area, enlargement, minArea, minEnlargement; + + while (true) { + path.push(node); + + if (node.leaf || path.length - 1 === level) break; + + minArea = minEnlargement = Infinity; + + for (i = 0, len = node.children.length; i < len; i++) { + child = node.children[i]; + area = bboxArea(child); + enlargement = enlargedArea(bbox, child) - area; + + // choose entry with the least area enlargement + if (enlargement < minEnlargement) { + minEnlargement = enlargement; + minArea = area < minArea ? area : minArea; + targetNode = child; + + } else if (enlargement === minEnlargement) { + // otherwise choose one with the smallest area + if (area < minArea) { + minArea = area; + targetNode = child; + } + } + } + + node = targetNode || node.children[0]; + } + + return node; + }, + + _insert: function (item, level, isNode) { + + var toBBox = this.toBBox, + bbox = isNode ? item : toBBox(item), + insertPath = []; + + // find the best node for accommodating the item, saving all nodes along the path too + var node = this._chooseSubtree(bbox, this.data, level, insertPath); + + // put the item into the node + node.children.push(item); + extend(node, bbox); + + // split on node overflow; propagate upwards if necessary + while (level >= 0) { + if (insertPath[level].children.length > this._maxEntries) { + this._split(insertPath, level); + level--; + } else break; + } + + // adjust bboxes along the insertion path + this._adjustParentBBoxes(bbox, insertPath, level); + }, + + // split overflowed node into two + _split: function (insertPath, level) { + + var node = insertPath[level], + M = node.children.length, + m = this._minEntries; + + this._chooseSplitAxis(node, m, M); + + var splitIndex = this._chooseSplitIndex(node, m, M); + + var newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex)); + newNode.height = node.height; + newNode.leaf = node.leaf; + + calcBBox(node, this.toBBox); + calcBBox(newNode, this.toBBox); + + if (level) insertPath[level - 1].children.push(newNode); + else this._splitRoot(node, newNode); + }, + + _splitRoot: function (node, newNode) { + // split root node + this.data = createNode([node, newNode]); + this.data.height = node.height + 1; + this.data.leaf = false; + calcBBox(this.data, this.toBBox); + }, + + _chooseSplitIndex: function (node, m, M) { + + var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index; + + minOverlap = minArea = Infinity; + + for (i = m; i <= M - m; i++) { + bbox1 = distBBox(node, 0, i, this.toBBox); + bbox2 = distBBox(node, i, M, this.toBBox); + + overlap = intersectionArea(bbox1, bbox2); + area = bboxArea(bbox1) + bboxArea(bbox2); + + // choose distribution with minimum overlap + if (overlap < minOverlap) { + minOverlap = overlap; + index = i; + + minArea = area < minArea ? area : minArea; + + } else if (overlap === minOverlap) { + // otherwise choose distribution with minimum area + if (area < minArea) { + minArea = area; + index = i; + } + } + } + + return index; + }, + + // sorts node children by the best axis for split + _chooseSplitAxis: function (node, m, M) { + + var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX, + compareMinY = node.leaf ? this.compareMinY : compareNodeMinY, + xMargin = this._allDistMargin(node, m, M, compareMinX), + yMargin = this._allDistMargin(node, m, M, compareMinY); + + // if total distributions margin value is minimal for x, sort by minX, + // otherwise it's already sorted by minY + if (xMargin < yMargin) node.children.sort(compareMinX); + }, + + // total margin of all possible split distributions where each node is at least m full + _allDistMargin: function (node, m, M, compare) { + + node.children.sort(compare); + + var toBBox = this.toBBox, + leftBBox = distBBox(node, 0, m, toBBox), + rightBBox = distBBox(node, M - m, M, toBBox), + margin = bboxMargin(leftBBox) + bboxMargin(rightBBox), + i, child; + + for (i = m; i < M - m; i++) { + child = node.children[i]; + extend(leftBBox, node.leaf ? toBBox(child) : child); + margin += bboxMargin(leftBBox); + } + + for (i = M - m - 1; i >= m; i--) { + child = node.children[i]; + extend(rightBBox, node.leaf ? toBBox(child) : child); + margin += bboxMargin(rightBBox); + } + + return margin; + }, + + _adjustParentBBoxes: function (bbox, path, level) { + // adjust bboxes along the given tree path + for (var i = level; i >= 0; i--) { + extend(path[i], bbox); + } + }, + + _condense: function (path) { + // go through the path, removing empty nodes and updating bboxes + for (var i = path.length - 1, siblings; i >= 0; i--) { + if (path[i].children.length === 0) { + if (i > 0) { + siblings = path[i - 1].children; + siblings.splice(siblings.indexOf(path[i]), 1); + + } else this.clear(); + + } else calcBBox(path[i], this.toBBox); + } + }, + + _initFormat: function (format) { + // data format (minX, minY, maxX, maxY accessors) + + // uses eval-type function compilation instead of just accepting a toBBox function + // because the algorithms are very sensitive to sorting functions performance, + // so they should be dead simple and without inner calls + + var compareArr = ['return a', ' - b', ';']; + + this.compareMinX = new Function('a', 'b', compareArr.join(format[0])); + this.compareMinY = new Function('a', 'b', compareArr.join(format[1])); + + this.toBBox = new Function('a', + 'return {minX: a' + format[0] + + ', minY: a' + format[1] + + ', maxX: a' + format[2] + + ', maxY: a' + format[3] + '};'); + } +}; + +function findItem(item, items, equalsFn) { + if (!equalsFn) return items.indexOf(item); + + for (var i = 0; i < items.length; i++) { + if (equalsFn(item, items[i])) return i; + } + return -1; +} + +// calculate node's bbox from bboxes of its children +function calcBBox(node, toBBox) { + distBBox(node, 0, node.children.length, toBBox, node); +} + +// min bounding rectangle of node children from k to p-1 +function distBBox(node, k, p, toBBox, destNode) { + if (!destNode) destNode = createNode(null); + destNode.minX = Infinity; + destNode.minY = Infinity; + destNode.maxX = -Infinity; + destNode.maxY = -Infinity; + + for (var i = k, child; i < p; i++) { + child = node.children[i]; + extend(destNode, node.leaf ? toBBox(child) : child); + } + + return destNode; +} + +function extend(a, b) { + a.minX = Math.min(a.minX, b.minX); + a.minY = Math.min(a.minY, b.minY); + a.maxX = Math.max(a.maxX, b.maxX); + a.maxY = Math.max(a.maxY, b.maxY); + return a; +} + +function compareNodeMinX(a, b) { return a.minX - b.minX; } +function compareNodeMinY(a, b) { return a.minY - b.minY; } + +function bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); } +function bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); } + +function enlargedArea(a, b) { + return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) * + (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY)); +} + +function intersectionArea(a, b) { + var minX = Math.max(a.minX, b.minX), + minY = Math.max(a.minY, b.minY), + maxX = Math.min(a.maxX, b.maxX), + maxY = Math.min(a.maxY, b.maxY); + + return Math.max(0, maxX - minX) * + Math.max(0, maxY - minY); +} + +function contains(a, b) { + return a.minX <= b.minX && + a.minY <= b.minY && + b.maxX <= a.maxX && + b.maxY <= a.maxY; +} + +function intersects(a, b) { + return b.minX <= a.maxX && + b.minY <= a.maxY && + b.maxX >= a.minX && + b.maxY >= a.minY; +} + +function createNode(children) { + return { + children: children, + height: 1, + leaf: true, + minX: Infinity, + minY: Infinity, + maxX: -Infinity, + maxY: -Infinity + }; +} + +// sort an array so that items come in groups of n unsorted items, with groups sorted between each other; +// combines selection algorithm with binary divide & conquer approach + +function multiSelect(arr, left, right, n, compare) { + var stack = [left, right], + mid; + + while (stack.length) { + right = stack.pop(); + left = stack.pop(); + + if (right - left <= n) continue; + + mid = left + Math.ceil((right - left) / n / 2) * n; + quickselect(arr, mid, left, right, compare); + + stack.push(left, mid, mid, right); + } +} + +},{"quickselect":1}]},{},[2])(2) +}); +ol.ext.rbush = module.exports; +})(); + +goog.provide('ol.structs.RBush'); + +goog.require('goog.asserts'); +goog.require('ol.ext.rbush'); +goog.require('ol.extent'); +goog.require('ol.object'); + + +/** + * Wrapper around the RBush by Vladimir Agafonkin. + * + * @constructor + * @param {number=} opt_maxEntries Max entries. + * @see https://github.com/mourner/rbush + * @struct + * @template T + */ +ol.structs.RBush = function(opt_maxEntries) { + + /** + * @private + */ + this.rbush_ = ol.ext.rbush(opt_maxEntries); + + /** + * A mapping between the objects added to this rbush wrapper + * and the objects that are actually added to the internal rbush. + * @private + * @type {Object.<number, ol.RBushEntry>} + */ + this.items_ = {}; + + if (goog.DEBUG) { + /** + * @private + * @type {number} + */ + this.readers_ = 0; + } +}; + + +/** + * Insert a value into the RBush. + * @param {ol.Extent} extent Extent. + * @param {T} value Value. + */ +ol.structs.RBush.prototype.insert = function(extent, value) { + if (goog.DEBUG && this.readers_) { + throw new Error('Can not insert value while reading'); + } + /** @type {ol.RBushEntry} */ + var item = { + minX: extent[0], + minY: extent[1], + maxX: extent[2], + maxY: extent[3], + value: value + }; + + this.rbush_.insert(item); + // remember the object that was added to the internal rbush + goog.asserts.assert(!(goog.getUid(value) in this.items_), + 'uid (%s) of value (%s) already exists', goog.getUid(value), value); + this.items_[goog.getUid(value)] = item; +}; + + +/** + * Bulk-insert values into the RBush. + * @param {Array.<ol.Extent>} extents Extents. + * @param {Array.<T>} values Values. + */ +ol.structs.RBush.prototype.load = function(extents, values) { + if (goog.DEBUG && this.readers_) { + throw new Error('Can not insert values while reading'); + } + goog.asserts.assert(extents.length === values.length, + 'extens and values must have same length (%s === %s)', + extents.length, values.length); + + var items = new Array(values.length); + for (var i = 0, l = values.length; i < l; i++) { + var extent = extents[i]; + var value = values[i]; + + /** @type {ol.RBushEntry} */ + var item = { + minX: extent[0], + minY: extent[1], + maxX: extent[2], + maxY: extent[3], + value: value + }; + items[i] = item; + goog.asserts.assert(!(goog.getUid(value) in this.items_), + 'uid (%s) of value (%s) already exists', goog.getUid(value), value); + this.items_[goog.getUid(value)] = item; + } + this.rbush_.load(items); +}; + + +/** + * Remove a value from the RBush. + * @param {T} value Value. + * @return {boolean} Removed. + */ +ol.structs.RBush.prototype.remove = function(value) { + if (goog.DEBUG && this.readers_) { + throw new Error('Can not remove value while reading'); + } + var uid = goog.getUid(value); + goog.asserts.assert(uid in this.items_, + 'uid (%s) of value (%s) does not exist', uid, value); + + // get the object in which the value was wrapped when adding to the + // internal rbush. then use that object to do the removal. + var item = this.items_[uid]; + delete this.items_[uid]; + return this.rbush_.remove(item) !== null; +}; + + +/** + * Update the extent of a value in the RBush. + * @param {ol.Extent} extent Extent. + * @param {T} value Value. + */ +ol.structs.RBush.prototype.update = function(extent, value) { + var uid = goog.getUid(value); + goog.asserts.assert(uid in this.items_, + 'uid (%s) of value (%s) does not exist', uid, value); + + var item = this.items_[uid]; + var bbox = [item.minX, item.minY, item.maxX, item.maxY]; + if (!ol.extent.equals(bbox, extent)) { + if (goog.DEBUG && this.readers_) { + throw new Error('Can not update extent while reading'); + } + this.remove(value); + this.insert(extent, value); + } +}; + + +/** + * Return all values in the RBush. + * @return {Array.<T>} All. + */ +ol.structs.RBush.prototype.getAll = function() { + var items = this.rbush_.all(); + return items.map(function(item) { + return item.value; + }); +}; + + +/** + * Return all values in the given extent. + * @param {ol.Extent} extent Extent. + * @return {Array.<T>} All in extent. + */ +ol.structs.RBush.prototype.getInExtent = function(extent) { + /** @type {ol.RBushEntry} */ + var bbox = { + minX: extent[0], + minY: extent[1], + maxX: extent[2], + maxY: extent[3] + }; + var items = this.rbush_.search(bbox); + return items.map(function(item) { + return item.value; + }); +}; + + +/** + * Calls a callback function with each value in the tree. + * If the callback returns a truthy value, this value is returned without + * checking the rest of the tree. + * @param {function(this: S, T): *} callback Callback. + * @param {S=} opt_this The object to use as `this` in `callback`. + * @return {*} Callback return value. + * @template S + */ +ol.structs.RBush.prototype.forEach = function(callback, opt_this) { + if (goog.DEBUG) { + ++this.readers_; + try { + return this.forEach_(this.getAll(), callback, opt_this); + } finally { + --this.readers_; + } + } else { + return this.forEach_(this.getAll(), callback, opt_this); + } +}; + + +/** + * Calls a callback function with each value in the provided extent. + * @param {ol.Extent} extent Extent. + * @param {function(this: S, T): *} callback Callback. + * @param {S=} opt_this The object to use as `this` in `callback`. + * @return {*} Callback return value. + * @template S + */ +ol.structs.RBush.prototype.forEachInExtent = function(extent, callback, opt_this) { + if (goog.DEBUG) { + ++this.readers_; + try { + return this.forEach_(this.getInExtent(extent), callback, opt_this); + } finally { + --this.readers_; + } + } else { + return this.forEach_(this.getInExtent(extent), callback, opt_this); + } +}; + + +/** + * @param {Array.<T>} values Values. + * @param {function(this: S, T): *} callback Callback. + * @param {S=} opt_this The object to use as `this` in `callback`. + * @private + * @return {*} Callback return value. + * @template S + */ +ol.structs.RBush.prototype.forEach_ = function(values, callback, opt_this) { + var result; + for (var i = 0, l = values.length; i < l; i++) { + result = callback.call(opt_this, values[i]); + if (result) { + return result; + } + } + return result; +}; + + +/** + * @return {boolean} Is empty. + */ +ol.structs.RBush.prototype.isEmpty = function() { + return ol.object.isEmpty(this.items_); +}; + + +/** + * Remove all values from the RBush. + */ +ol.structs.RBush.prototype.clear = function() { + this.rbush_.clear(); + this.items_ = {}; +}; + + +/** + * @param {ol.Extent=} opt_extent Extent. + * @return {!ol.Extent} Extent. + */ +ol.structs.RBush.prototype.getExtent = function(opt_extent) { + // FIXME add getExtent() to rbush + var data = this.rbush_.data; + return [data.minX, data.minY, data.maxX, data.maxY]; +}; + +// FIXME bulk feature upload - suppress events +// FIXME make change-detection more refined (notably, geometry hint) + +goog.provide('ol.source.Vector'); +goog.provide('ol.source.VectorEvent'); +goog.provide('ol.source.VectorEventType'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Feature'); +goog.require('ol.ObjectEventType'); +goog.require('ol.array'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.featureloader'); +goog.require('ol.loadingstrategy'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.source.Source'); +goog.require('ol.source.State'); +goog.require('ol.structs.RBush'); + + +/** + * @enum {string} + */ +ol.source.VectorEventType = { + /** + * Triggered when a feature is added to the source. + * @event ol.source.VectorEvent#addfeature + * @api stable + */ + ADDFEATURE: 'addfeature', + + /** + * Triggered when a feature is updated. + * @event ol.source.VectorEvent#changefeature + * @api + */ + CHANGEFEATURE: 'changefeature', + + /** + * Triggered when the clear method is called on the source. + * @event ol.source.VectorEvent#clear + * @api + */ + CLEAR: 'clear', + + /** + * Triggered when a feature is removed from the source. + * See {@link ol.source.Vector#clear source.clear()} for exceptions. + * @event ol.source.VectorEvent#removefeature + * @api stable + */ + REMOVEFEATURE: 'removefeature' +}; + + +/** + * @classdesc + * Provides a source of features for vector layers. Vector features provided + * by this source are suitable for editing. See {@link ol.source.VectorTile} for + * vector data that is optimized for rendering. + * + * @constructor + * @extends {ol.source.Source} + * @fires ol.source.VectorEvent + * @param {olx.source.VectorOptions=} opt_options Vector source options. + * @api stable + */ +ol.source.Vector = function(opt_options) { + + var options = opt_options || {}; + + ol.source.Source.call(this, { + attributions: options.attributions, + logo: options.logo, + projection: undefined, + state: ol.source.State.READY, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); + + /** + * @private + * @type {ol.FeatureLoader} + */ + this.loader_ = ol.nullFunction; + + /** + * @private + * @type {ol.format.Feature|undefined} + */ + this.format_ = options.format; + + /** + * @private + * @type {string|ol.FeatureUrlFunction|undefined} + */ + this.url_ = options.url; + + if (options.loader !== undefined) { + this.loader_ = options.loader; + } else if (this.url_ !== undefined) { + goog.asserts.assert(this.format_ !== undefined, + 'format must be set when url is set'); + // create a XHR feature loader for "url" and "format" + this.loader_ = ol.featureloader.xhr(this.url_, this.format_); + } + + /** + * @private + * @type {ol.LoadingStrategy} + */ + this.strategy_ = options.strategy !== undefined ? options.strategy : + ol.loadingstrategy.all; + + var useSpatialIndex = + options.useSpatialIndex !== undefined ? options.useSpatialIndex : true; + + /** + * @private + * @type {ol.structs.RBush.<ol.Feature>} + */ + this.featuresRtree_ = useSpatialIndex ? new ol.structs.RBush() : null; + + /** + * @private + * @type {ol.structs.RBush.<{extent: ol.Extent}>} + */ + this.loadedExtentsRtree_ = new ol.structs.RBush(); + + /** + * @private + * @type {Object.<string, ol.Feature>} + */ + this.nullGeometryFeatures_ = {}; + + /** + * A lookup of features by id (the return from feature.getId()). + * @private + * @type {Object.<string, ol.Feature>} + */ + this.idIndex_ = {}; + + /** + * A lookup of features without id (keyed by goog.getUid(feature)). + * @private + * @type {Object.<string, ol.Feature>} + */ + this.undefIdIndex_ = {}; + + /** + * @private + * @type {Object.<string, Array.<ol.EventsKey>>} + */ + this.featureChangeKeys_ = {}; + + /** + * @private + * @type {ol.Collection.<ol.Feature>} + */ + this.featuresCollection_ = null; + + var collection, features; + if (options.features instanceof ol.Collection) { + collection = options.features; + features = collection.getArray(); + } else if (Array.isArray(options.features)) { + features = options.features; + } + if (!useSpatialIndex && collection === undefined) { + collection = new ol.Collection(features); + } + if (features !== undefined) { + this.addFeaturesInternal(features); + } + if (collection !== undefined) { + this.bindFeaturesCollection_(collection); + } + +}; +ol.inherits(ol.source.Vector, ol.source.Source); + + +/** + * Add a single feature to the source. If you want to add a batch of features + * at once, call {@link ol.source.Vector#addFeatures source.addFeatures()} + * instead. + * @param {ol.Feature} feature Feature to add. + * @api stable + */ +ol.source.Vector.prototype.addFeature = function(feature) { + this.addFeatureInternal(feature); + this.changed(); +}; + + +/** + * Add a feature without firing a `change` event. + * @param {ol.Feature} feature Feature. + * @protected + */ +ol.source.Vector.prototype.addFeatureInternal = function(feature) { + var featureKey = goog.getUid(feature).toString(); + + if (!this.addToIndex_(featureKey, feature)) { + return; + } + + this.setupChangeEvents_(featureKey, feature); + + var geometry = feature.getGeometry(); + if (geometry) { + var extent = geometry.getExtent(); + if (this.featuresRtree_) { + this.featuresRtree_.insert(extent, feature); + } + } else { + this.nullGeometryFeatures_[featureKey] = feature; + } + + this.dispatchEvent( + new ol.source.VectorEvent(ol.source.VectorEventType.ADDFEATURE, feature)); +}; + + +/** + * @param {string} featureKey Unique identifier for the feature. + * @param {ol.Feature} feature The feature. + * @private + */ +ol.source.Vector.prototype.setupChangeEvents_ = function(featureKey, feature) { + goog.asserts.assert(!(featureKey in this.featureChangeKeys_), + 'key (%s) not yet registered in featureChangeKey', featureKey); + this.featureChangeKeys_[featureKey] = [ + ol.events.listen(feature, ol.events.EventType.CHANGE, + this.handleFeatureChange_, this), + ol.events.listen(feature, ol.ObjectEventType.PROPERTYCHANGE, + this.handleFeatureChange_, this) + ]; +}; + + +/** + * @param {string} featureKey Unique identifier for the feature. + * @param {ol.Feature} feature The feature. + * @return {boolean} The feature is "valid", in the sense that it is also a + * candidate for insertion into the Rtree. + * @private + */ +ol.source.Vector.prototype.addToIndex_ = function(featureKey, feature) { + var valid = true; + var id = feature.getId(); + if (id !== undefined) { + if (!(id.toString() in this.idIndex_)) { + this.idIndex_[id.toString()] = feature; + } else { + valid = false; + } + } else { + goog.asserts.assert(!(featureKey in this.undefIdIndex_), + 'Feature already added to the source'); + this.undefIdIndex_[featureKey] = feature; + } + return valid; +}; + + +/** + * Add a batch of features to the source. + * @param {Array.<ol.Feature>} features Features to add. + * @api stable + */ +ol.source.Vector.prototype.addFeatures = function(features) { + this.addFeaturesInternal(features); + this.changed(); +}; + + +/** + * Add features without firing a `change` event. + * @param {Array.<ol.Feature>} features Features. + * @protected + */ +ol.source.Vector.prototype.addFeaturesInternal = function(features) { + var featureKey, i, length, feature; + + var extents = []; + var newFeatures = []; + var geometryFeatures = []; + + for (i = 0, length = features.length; i < length; i++) { + feature = features[i]; + featureKey = goog.getUid(feature).toString(); + if (this.addToIndex_(featureKey, feature)) { + newFeatures.push(feature); + } + } + + for (i = 0, length = newFeatures.length; i < length; i++) { + feature = newFeatures[i]; + featureKey = goog.getUid(feature).toString(); + this.setupChangeEvents_(featureKey, feature); + + var geometry = feature.getGeometry(); + if (geometry) { + var extent = geometry.getExtent(); + extents.push(extent); + geometryFeatures.push(feature); + } else { + this.nullGeometryFeatures_[featureKey] = feature; + } + } + if (this.featuresRtree_) { + this.featuresRtree_.load(extents, geometryFeatures); + } + + for (i = 0, length = newFeatures.length; i < length; i++) { + this.dispatchEvent(new ol.source.VectorEvent( + ol.source.VectorEventType.ADDFEATURE, newFeatures[i])); + } +}; + + +/** + * @param {!ol.Collection.<ol.Feature>} collection Collection. + * @private + */ +ol.source.Vector.prototype.bindFeaturesCollection_ = function(collection) { + goog.asserts.assert(!this.featuresCollection_, + 'bindFeaturesCollection can only be called once'); + var modifyingCollection = false; + ol.events.listen(this, ol.source.VectorEventType.ADDFEATURE, + function(evt) { + if (!modifyingCollection) { + modifyingCollection = true; + collection.push(evt.feature); + modifyingCollection = false; + } + }); + ol.events.listen(this, ol.source.VectorEventType.REMOVEFEATURE, + function(evt) { + if (!modifyingCollection) { + modifyingCollection = true; + collection.remove(evt.feature); + modifyingCollection = false; + } + }); + ol.events.listen(collection, ol.CollectionEventType.ADD, + function(evt) { + if (!modifyingCollection) { + var feature = evt.element; + goog.asserts.assertInstanceof(feature, ol.Feature); + modifyingCollection = true; + this.addFeature(feature); + modifyingCollection = false; + } + }, this); + ol.events.listen(collection, ol.CollectionEventType.REMOVE, + function(evt) { + if (!modifyingCollection) { + var feature = evt.element; + goog.asserts.assertInstanceof(feature, ol.Feature); + modifyingCollection = true; + this.removeFeature(feature); + modifyingCollection = false; + } + }, this); + this.featuresCollection_ = collection; +}; + + +/** + * Remove all features from the source. + * @param {boolean=} opt_fast Skip dispatching of {@link removefeature} events. + * @api stable + */ +ol.source.Vector.prototype.clear = function(opt_fast) { + if (opt_fast) { + for (var featureId in this.featureChangeKeys_) { + var keys = this.featureChangeKeys_[featureId]; + keys.forEach(ol.events.unlistenByKey); + } + if (!this.featuresCollection_) { + this.featureChangeKeys_ = {}; + this.idIndex_ = {}; + this.undefIdIndex_ = {}; + } + } else { + if (this.featuresRtree_) { + this.featuresRtree_.forEach(this.removeFeatureInternal, this); + for (var id in this.nullGeometryFeatures_) { + this.removeFeatureInternal(this.nullGeometryFeatures_[id]); + } + } + } + if (this.featuresCollection_) { + this.featuresCollection_.clear(); + } + goog.asserts.assert(ol.object.isEmpty(this.featureChangeKeys_), + 'featureChangeKeys is an empty object now'); + goog.asserts.assert(ol.object.isEmpty(this.idIndex_), + 'idIndex is an empty object now'); + goog.asserts.assert(ol.object.isEmpty(this.undefIdIndex_), + 'undefIdIndex is an empty object now'); + + if (this.featuresRtree_) { + this.featuresRtree_.clear(); + } + this.loadedExtentsRtree_.clear(); + this.nullGeometryFeatures_ = {}; + + var clearEvent = new ol.source.VectorEvent(ol.source.VectorEventType.CLEAR); + this.dispatchEvent(clearEvent); + this.changed(); +}; + + +/** + * Iterate through all features on the source, calling the provided callback + * with each one. If the callback returns any "truthy" value, iteration will + * stop and the function will return the same value. + * + * @param {function(this: T, ol.Feature): S} callback Called with each feature + * on the source. Return a truthy value to stop iteration. + * @param {T=} opt_this The object to use as `this` in the callback. + * @return {S|undefined} The return value from the last call to the callback. + * @template T,S + * @api stable + */ +ol.source.Vector.prototype.forEachFeature = function(callback, opt_this) { + if (this.featuresRtree_) { + return this.featuresRtree_.forEach(callback, opt_this); + } else if (this.featuresCollection_) { + return this.featuresCollection_.forEach(callback, opt_this); + } +}; + + +/** + * Iterate through all features whose geometries contain the provided + * coordinate, calling the callback with each feature. If the callback returns + * a "truthy" value, iteration will stop and the function will return the same + * value. + * + * @param {ol.Coordinate} coordinate Coordinate. + * @param {function(this: T, ol.Feature): S} callback Called with each feature + * whose goemetry contains the provided coordinate. + * @param {T=} opt_this The object to use as `this` in the callback. + * @return {S|undefined} The return value from the last call to the callback. + * @template T,S + */ +ol.source.Vector.prototype.forEachFeatureAtCoordinateDirect = function(coordinate, callback, opt_this) { + var extent = [coordinate[0], coordinate[1], coordinate[0], coordinate[1]]; + return this.forEachFeatureInExtent(extent, function(feature) { + var geometry = feature.getGeometry(); + goog.asserts.assert(geometry, 'feature geometry is defined and not null'); + if (geometry.containsCoordinate(coordinate)) { + return callback.call(opt_this, feature); + } else { + return undefined; + } + }); +}; + + +/** + * Iterate through all features whose bounding box intersects the provided + * extent (note that the feature's geometry may not intersect the extent), + * calling the callback with each feature. If the callback returns a "truthy" + * value, iteration will stop and the function will return the same value. + * + * If you are interested in features whose geometry intersects an extent, call + * the {@link ol.source.Vector#forEachFeatureIntersectingExtent + * source.forEachFeatureIntersectingExtent()} method instead. + * + * When `useSpatialIndex` is set to false, this method will loop through all + * features, equivalent to {@link ol.source.Vector#forEachFeature}. + * + * @param {ol.Extent} extent Extent. + * @param {function(this: T, ol.Feature): S} callback Called with each feature + * whose bounding box intersects the provided extent. + * @param {T=} opt_this The object to use as `this` in the callback. + * @return {S|undefined} The return value from the last call to the callback. + * @template T,S + * @api + */ +ol.source.Vector.prototype.forEachFeatureInExtent = function(extent, callback, opt_this) { + if (this.featuresRtree_) { + return this.featuresRtree_.forEachInExtent(extent, callback, opt_this); + } else if (this.featuresCollection_) { + return this.featuresCollection_.forEach(callback, opt_this); + } +}; + + +/** + * Iterate through all features whose geometry intersects the provided extent, + * calling the callback with each feature. If the callback returns a "truthy" + * value, iteration will stop and the function will return the same value. + * + * If you only want to test for bounding box intersection, call the + * {@link ol.source.Vector#forEachFeatureInExtent + * source.forEachFeatureInExtent()} method instead. + * + * @param {ol.Extent} extent Extent. + * @param {function(this: T, ol.Feature): S} callback Called with each feature + * whose geometry intersects the provided extent. + * @param {T=} opt_this The object to use as `this` in the callback. + * @return {S|undefined} The return value from the last call to the callback. + * @template T,S + * @api + */ +ol.source.Vector.prototype.forEachFeatureIntersectingExtent = function(extent, callback, opt_this) { + return this.forEachFeatureInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + * @return {S|undefined} The return value from the last call to the callback. + * @template S + */ + function(feature) { + var geometry = feature.getGeometry(); + goog.asserts.assert(geometry, + 'feature geometry is defined and not null'); + if (geometry.intersectsExtent(extent)) { + var result = callback.call(opt_this, feature); + if (result) { + return result; + } + } + }); +}; + + +/** + * Get the features collection associated with this source. Will be `null` + * unless the source was configured with `useSpatialIndex` set to `false`, or + * with an {@link ol.Collection} as `features`. + * @return {ol.Collection.<ol.Feature>} The collection of features. + * @api + */ +ol.source.Vector.prototype.getFeaturesCollection = function() { + return this.featuresCollection_; +}; + + +/** + * Get all features on the source. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.source.Vector.prototype.getFeatures = function() { + var features; + if (this.featuresCollection_) { + features = this.featuresCollection_.getArray(); + } else if (this.featuresRtree_) { + features = this.featuresRtree_.getAll(); + if (!ol.object.isEmpty(this.nullGeometryFeatures_)) { + ol.array.extend( + features, ol.object.getValues(this.nullGeometryFeatures_)); + } + } + goog.asserts.assert(features !== undefined, + 'Neither featuresRtree_ nor featuresCollection_ are available'); + return features; +}; + + +/** + * Get all features whose geometry intersects the provided coordinate. + * @param {ol.Coordinate} coordinate Coordinate. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.source.Vector.prototype.getFeaturesAtCoordinate = function(coordinate) { + var features = []; + this.forEachFeatureAtCoordinateDirect(coordinate, function(feature) { + features.push(feature); + }); + return features; +}; + + +/** + * Get all features in the provided extent. Note that this returns all features + * whose bounding boxes intersect the given extent (so it may include features + * whose geometries do not intersect the extent). + * + * This method is not available when the source is configured with + * `useSpatialIndex` set to `false`. + * @param {ol.Extent} extent Extent. + * @return {Array.<ol.Feature>} Features. + * @api + */ +ol.source.Vector.prototype.getFeaturesInExtent = function(extent) { + goog.asserts.assert(this.featuresRtree_, + 'getFeaturesInExtent does not work when useSpatialIndex is set to false'); + return this.featuresRtree_.getInExtent(extent); +}; + + +/** + * Get the closest feature to the provided coordinate. + * + * This method is not available when the source is configured with + * `useSpatialIndex` set to `false`. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {function(ol.Feature):boolean=} opt_filter Feature filter function. + * The filter function will receive one argument, the {@link ol.Feature feature} + * and it should return a boolean value. By default, no filtering is made. + * @return {ol.Feature} Closest feature. + * @api stable + */ +ol.source.Vector.prototype.getClosestFeatureToCoordinate = function(coordinate, opt_filter) { + // Find the closest feature using branch and bound. We start searching an + // infinite extent, and find the distance from the first feature found. This + // becomes the closest feature. We then compute a smaller extent which any + // closer feature must intersect. We continue searching with this smaller + // extent, trying to find a closer feature. Every time we find a closer + // feature, we update the extent being searched so that any even closer + // feature must intersect it. We continue until we run out of features. + var x = coordinate[0]; + var y = coordinate[1]; + var closestFeature = null; + var closestPoint = [NaN, NaN]; + var minSquaredDistance = Infinity; + var extent = [-Infinity, -Infinity, Infinity, Infinity]; + goog.asserts.assert(this.featuresRtree_, + 'getClosestFeatureToCoordinate does not work with useSpatialIndex set ' + + 'to false'); + var filter = opt_filter ? opt_filter : ol.functions.TRUE; + this.featuresRtree_.forEachInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + if (filter(feature)) { + var geometry = feature.getGeometry(); + goog.asserts.assert(geometry, + 'feature geometry is defined and not null'); + var previousMinSquaredDistance = minSquaredDistance; + minSquaredDistance = geometry.closestPointXY( + x, y, closestPoint, minSquaredDistance); + if (minSquaredDistance < previousMinSquaredDistance) { + closestFeature = feature; + // This is sneaky. Reduce the extent that it is currently being + // searched while the R-Tree traversal using this same extent object + // is still in progress. This is safe because the new extent is + // strictly contained by the old extent. + var minDistance = Math.sqrt(minSquaredDistance); + extent[0] = x - minDistance; + extent[1] = y - minDistance; + extent[2] = x + minDistance; + extent[3] = y + minDistance; + } + } + }); + return closestFeature; +}; + + +/** + * Get the extent of the features currently in the source. + * + * This method is not available when the source is configured with + * `useSpatialIndex` set to `false`. + * @return {!ol.Extent} Extent. + * @api stable + */ +ol.source.Vector.prototype.getExtent = function() { + goog.asserts.assert(this.featuresRtree_, + 'getExtent does not work when useSpatialIndex is set to false'); + return this.featuresRtree_.getExtent(); +}; + + +/** + * Get a feature by its identifier (the value returned by feature.getId()). + * Note that the index treats string and numeric identifiers as the same. So + * `source.getFeatureById(2)` will return a feature with id `'2'` or `2`. + * + * @param {string|number} id Feature identifier. + * @return {ol.Feature} The feature (or `null` if not found). + * @api stable + */ +ol.source.Vector.prototype.getFeatureById = function(id) { + var feature = this.idIndex_[id.toString()]; + return feature !== undefined ? feature : null; +}; + + +/** + * Get the format associated with this source. + * + * @return {ol.format.Feature|undefined} The feature format. + * @api + */ +ol.source.Vector.prototype.getFormat = function() { + return this.format_; +}; + + +/** + * Get the url associated with this source. + * + * @return {string|ol.FeatureUrlFunction|undefined} The url. + * @api + */ +ol.source.Vector.prototype.getUrl = function() { + return this.url_; +}; + + +/** + * @param {ol.events.Event} event Event. + * @private + */ +ol.source.Vector.prototype.handleFeatureChange_ = function(event) { + var feature = /** @type {ol.Feature} */ (event.target); + var featureKey = goog.getUid(feature).toString(); + var geometry = feature.getGeometry(); + if (!geometry) { + if (!(featureKey in this.nullGeometryFeatures_)) { + if (this.featuresRtree_) { + this.featuresRtree_.remove(feature); + } + this.nullGeometryFeatures_[featureKey] = feature; + } + } else { + var extent = geometry.getExtent(); + if (featureKey in this.nullGeometryFeatures_) { + delete this.nullGeometryFeatures_[featureKey]; + if (this.featuresRtree_) { + this.featuresRtree_.insert(extent, feature); + } + } else { + if (this.featuresRtree_) { + this.featuresRtree_.update(extent, feature); + } + } + } + var id = feature.getId(); + var removed; + if (id !== undefined) { + var sid = id.toString(); + if (featureKey in this.undefIdIndex_) { + delete this.undefIdIndex_[featureKey]; + this.idIndex_[sid] = feature; + } else { + if (this.idIndex_[sid] !== feature) { + removed = this.removeFromIdIndex_(feature); + goog.asserts.assert(removed, + 'Expected feature to be removed from index'); + this.idIndex_[sid] = feature; + } + } + } else { + if (!(featureKey in this.undefIdIndex_)) { + removed = this.removeFromIdIndex_(feature); + goog.asserts.assert(removed, + 'Expected feature to be removed from index'); + this.undefIdIndex_[featureKey] = feature; + } else { + goog.asserts.assert(this.undefIdIndex_[featureKey] === feature, + 'feature keyed under %s in undefIdKeys', featureKey); + } + } + this.changed(); + this.dispatchEvent(new ol.source.VectorEvent( + ol.source.VectorEventType.CHANGEFEATURE, feature)); +}; + + +/** + * @return {boolean} Is empty. + */ +ol.source.Vector.prototype.isEmpty = function() { + return this.featuresRtree_.isEmpty() && + ol.object.isEmpty(this.nullGeometryFeatures_); +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {ol.proj.Projection} projection Projection. + */ +ol.source.Vector.prototype.loadFeatures = function( + extent, resolution, projection) { + var loadedExtentsRtree = this.loadedExtentsRtree_; + var extentsToLoad = this.strategy_(extent, resolution); + var i, ii; + for (i = 0, ii = extentsToLoad.length; i < ii; ++i) { + var extentToLoad = extentsToLoad[i]; + var alreadyLoaded = loadedExtentsRtree.forEachInExtent(extentToLoad, + /** + * @param {{extent: ol.Extent}} object Object. + * @return {boolean} Contains. + */ + function(object) { + return ol.extent.containsExtent(object.extent, extentToLoad); + }); + if (!alreadyLoaded) { + this.loader_.call(this, extentToLoad, resolution, projection); + loadedExtentsRtree.insert(extentToLoad, {extent: extentToLoad.slice()}); + } + } +}; + + +/** + * Remove a single feature from the source. If you want to remove all features + * at once, use the {@link ol.source.Vector#clear source.clear()} method + * instead. + * @param {ol.Feature} feature Feature to remove. + * @api stable + */ +ol.source.Vector.prototype.removeFeature = function(feature) { + var featureKey = goog.getUid(feature).toString(); + if (featureKey in this.nullGeometryFeatures_) { + delete this.nullGeometryFeatures_[featureKey]; + } else { + if (this.featuresRtree_) { + this.featuresRtree_.remove(feature); + } + } + this.removeFeatureInternal(feature); + this.changed(); +}; + + +/** + * Remove feature without firing a `change` event. + * @param {ol.Feature} feature Feature. + * @protected + */ +ol.source.Vector.prototype.removeFeatureInternal = function(feature) { + var featureKey = goog.getUid(feature).toString(); + goog.asserts.assert(featureKey in this.featureChangeKeys_, + 'featureKey exists in featureChangeKeys'); + this.featureChangeKeys_[featureKey].forEach(ol.events.unlistenByKey); + delete this.featureChangeKeys_[featureKey]; + var id = feature.getId(); + if (id !== undefined) { + delete this.idIndex_[id.toString()]; + } else { + delete this.undefIdIndex_[featureKey]; + } + this.dispatchEvent(new ol.source.VectorEvent( + ol.source.VectorEventType.REMOVEFEATURE, feature)); +}; + + +/** + * Remove a feature from the id index. Called internally when the feature id + * may have changed. + * @param {ol.Feature} feature The feature. + * @return {boolean} Removed the feature from the index. + * @private + */ +ol.source.Vector.prototype.removeFromIdIndex_ = function(feature) { + var removed = false; + for (var id in this.idIndex_) { + if (this.idIndex_[id] === feature) { + delete this.idIndex_[id]; + removed = true; + break; + } + } + return removed; +}; + + +/** + * @classdesc + * Events emitted by {@link ol.source.Vector} instances are instances of this + * type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.source.VectorEvent} + * @param {string} type Type. + * @param {ol.Feature=} opt_feature Feature. + */ +ol.source.VectorEvent = function(type, opt_feature) { + + ol.events.Event.call(this, type); + + /** + * The feature being added or removed. + * @type {ol.Feature|undefined} + * @api stable + */ + this.feature = opt_feature; + +}; +ol.inherits(ol.source.VectorEvent, ol.events.Event); + +goog.provide('ol.source.ImageVector'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('goog.vec.Mat4'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.render.canvas.ReplayGroup'); +goog.require('ol.renderer.vector'); +goog.require('ol.source.ImageCanvas'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Style'); +goog.require('ol.vec.Mat4'); + + +/** + * @classdesc + * An image source whose images are canvas elements into which vector features + * read from a vector source (`ol.source.Vector`) are drawn. An + * `ol.source.ImageVector` object is to be used as the `source` of an image + * layer (`ol.layer.Image`). Image layers are rotated, scaled, and translated, + * as opposed to being re-rendered, during animations and interactions. So, like + * any other image layer, an image layer configured with an + * `ol.source.ImageVector` will exhibit this behaviour. This is in contrast to a + * vector layer, where vector features are re-drawn during animations and + * interactions. + * + * @constructor + * @extends {ol.source.ImageCanvas} + * @param {olx.source.ImageVectorOptions} options Options. + * @api + */ +ol.source.ImageVector = function(options) { + + /** + * @private + * @type {ol.source.Vector} + */ + this.source_ = options.source; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.canvasContext_ = ol.dom.createCanvasContext2D(); + + /** + * @private + * @type {ol.Size} + */ + this.canvasSize_ = [0, 0]; + + /** + * @private + * @type {ol.render.canvas.ReplayGroup} + */ + this.replayGroup_ = null; + + ol.source.ImageCanvas.call(this, { + attributions: options.attributions, + canvasFunction: this.canvasFunctionInternal_.bind(this), + logo: options.logo, + projection: options.projection, + ratio: options.ratio, + resolutions: options.resolutions, + state: this.source_.getState() + }); + + /** + * User provided style. + * @type {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} + * @private + */ + this.style_ = null; + + /** + * Style function for use within the library. + * @type {ol.StyleFunction|undefined} + * @private + */ + this.styleFunction_ = undefined; + + this.setStyle(options.style); + + ol.events.listen(this.source_, ol.events.EventType.CHANGE, + this.handleSourceChange_, this); + +}; +ol.inherits(ol.source.ImageVector, ol.source.ImageCanvas); + + +/** + * @param {ol.Extent} extent Extent. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.Size} size Size. + * @param {ol.proj.Projection} projection Projection; + * @return {HTMLCanvasElement} Canvas element. + * @private + */ +ol.source.ImageVector.prototype.canvasFunctionInternal_ = function(extent, resolution, pixelRatio, size, projection) { + + var replayGroup = new ol.render.canvas.ReplayGroup( + ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, + resolution); + + this.source_.loadFeatures(extent, resolution, projection); + + var loading = false; + this.source_.forEachFeatureInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + loading = loading || + this.renderFeature_(feature, resolution, pixelRatio, replayGroup); + }, this); + replayGroup.finish(); + + if (loading) { + return null; + } + + if (this.canvasSize_[0] != size[0] || this.canvasSize_[1] != size[1]) { + this.canvasContext_.canvas.width = size[0]; + this.canvasContext_.canvas.height = size[1]; + this.canvasSize_[0] = size[0]; + this.canvasSize_[1] = size[1]; + } else { + this.canvasContext_.clearRect(0, 0, size[0], size[1]); + } + + var transform = this.getTransform_(ol.extent.getCenter(extent), + resolution, pixelRatio, size); + replayGroup.replay(this.canvasContext_, pixelRatio, transform, 0, {}); + + this.replayGroup_ = replayGroup; + + return this.canvasContext_.canvas; +}; + + +/** + * @inheritDoc + */ +ol.source.ImageVector.prototype.forEachFeatureAtCoordinate = function( + coordinate, resolution, rotation, skippedFeatureUids, callback) { + if (!this.replayGroup_) { + return undefined; + } else { + /** @type {Object.<string, boolean>} */ + var features = {}; + return this.replayGroup_.forEachFeatureAtCoordinate( + coordinate, resolution, 0, skippedFeatureUids, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + goog.asserts.assert(feature !== undefined, 'passed a feature'); + var key = goog.getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback(feature); + } + }); + } +}; + + +/** + * Get a reference to the wrapped source. + * @return {ol.source.Vector} Source. + * @api + */ +ol.source.ImageVector.prototype.getSource = function() { + return this.source_; +}; + + +/** + * Get the style for features. This returns whatever was passed to the `style` + * option at construction or to the `setStyle` method. + * @return {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction} + * Layer style. + * @api stable + */ +ol.source.ImageVector.prototype.getStyle = function() { + return this.style_; +}; + + +/** + * Get the style function. + * @return {ol.StyleFunction|undefined} Layer style function. + * @api stable + */ +ol.source.ImageVector.prototype.getStyleFunction = function() { + return this.styleFunction_; +}; + + +/** + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.Size} size Size. + * @return {!goog.vec.Mat4.Number} Transform. + * @private + */ +ol.source.ImageVector.prototype.getTransform_ = function(center, resolution, pixelRatio, size) { + return ol.vec.Mat4.makeTransform2D(this.transform_, + size[0] / 2, size[1] / 2, + pixelRatio / resolution, -pixelRatio / resolution, + 0, + -center[0], -center[1]); +}; + + +/** + * Handle changes in image style state. + * @param {ol.events.Event} event Image style change event. + * @private + */ +ol.source.ImageVector.prototype.handleImageChange_ = function(event) { + this.changed(); +}; + + +/** + * @private + */ +ol.source.ImageVector.prototype.handleSourceChange_ = function() { + // setState will trigger a CHANGE event, so we always rely + // change events by calling setState. + this.setState(this.source_.getState()); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. + * @private + */ +ol.source.ImageVector.prototype.renderFeature_ = function(feature, resolution, pixelRatio, replayGroup) { + var styles; + var styleFunction = feature.getStyleFunction(); + if (styleFunction) { + styles = styleFunction.call(feature, resolution); + } else if (this.styleFunction_) { + styles = this.styleFunction_(feature, resolution); + } + if (!styles) { + return false; + } + var i, ii, loading = false; + if (!Array.isArray(styles)) { + styles = [styles]; + } + for (i = 0, ii = styles.length; i < ii; ++i) { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles[i], + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + this.handleImageChange_, this) || loading; + } + return loading; +}; + + +/** + * Set the style for features. This can be a single style object, an array + * of styles, or a function that takes a feature and resolution and returns + * an array of styles. If it is `undefined` the default style is used. If + * it is `null` the layer has no style (a `null` style), so only features + * that have their own styles will be rendered in the layer. See + * {@link ol.style} for information on the default style. + * @param {ol.style.Style|Array.<ol.style.Style>|ol.StyleFunction|undefined} + * style Layer style. + * @api stable + */ +ol.source.ImageVector.prototype.setStyle = function(style) { + this.style_ = style !== undefined ? style : ol.style.defaultStyleFunction; + this.styleFunction_ = !style ? + undefined : ol.style.createStyleFunction(this.style_); + this.changed(); +}; + +goog.provide('ol.renderer.canvas.ImageLayer'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol.functions'); +goog.require('ol.ImageBase'); +goog.require('ol.ViewHint'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.layer.Image'); +goog.require('ol.proj'); +goog.require('ol.renderer.canvas.Layer'); +goog.require('ol.source.ImageVector'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.canvas.Layer} + * @param {ol.layer.Image} imageLayer Single image layer. + */ +ol.renderer.canvas.ImageLayer = function(imageLayer) { + + ol.renderer.canvas.Layer.call(this, imageLayer); + + /** + * @private + * @type {?ol.ImageBase} + */ + this.image_ = null; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.imageTransform_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {?goog.vec.Mat4.Number} + */ + this.imageTransformInv_ = null; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.hitCanvasContext_ = null; + +}; +ol.inherits(ol.renderer.canvas.ImageLayer, ol.renderer.canvas.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg) { + var layer = this.getLayer(); + var source = layer.getSource(); + var resolution = frameState.viewState.resolution; + var rotation = frameState.viewState.rotation; + var skippedFeatureUids = frameState.skippedFeatureUids; + return source.forEachFeatureAtCoordinate( + coordinate, resolution, rotation, skippedFeatureUids, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(thisArg, feature, layer); + }); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { + if (!this.getImage()) { + return undefined; + } + + if (this.getLayer().getSource() instanceof ol.source.ImageVector) { + // for ImageVector sources use the original hit-detection logic, + // so that for example also transparent polygons are detected + var coordinate = pixel.slice(); + ol.vec.Mat4.multVec2( + frameState.pixelToCoordinateMatrix, coordinate, coordinate); + var hasFeature = this.forEachFeatureAtCoordinate( + coordinate, frameState, ol.functions.TRUE, this); + + if (hasFeature) { + return callback.call(thisArg, this.getLayer()); + } else { + return undefined; + } + } else { + // for all other image sources directly check the image + if (!this.imageTransformInv_) { + this.imageTransformInv_ = goog.vec.Mat4.createNumber(); + goog.vec.Mat4.invert(this.imageTransform_, this.imageTransformInv_); + } + + var pixelOnCanvas = + this.getPixelOnCanvas(pixel, this.imageTransformInv_); + + if (!this.hitCanvasContext_) { + this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1); + } + + this.hitCanvasContext_.clearRect(0, 0, 1, 1); + this.hitCanvasContext_.drawImage( + this.getImage(), pixelOnCanvas[0], pixelOnCanvas[1], 1, 1, 0, 0, 1, 1); + + var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; + if (imageData[3] > 0) { + return callback.call(thisArg, this.getLayer()); + } else { + return undefined; + } + } +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.getImage = function() { + return !this.image_ ? null : this.image_.getImage(); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.getImageTransform = function() { + return this.imageTransform_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, layerState) { + + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var viewCenter = viewState.center; + var viewResolution = viewState.resolution; + + var image; + var imageLayer = this.getLayer(); + goog.asserts.assertInstanceof(imageLayer, ol.layer.Image, + 'layer is an instance of ol.layer.Image'); + var imageSource = imageLayer.getSource(); + + var hints = frameState.viewHints; + + var renderedExtent = frameState.extent; + if (layerState.extent !== undefined) { + renderedExtent = ol.extent.getIntersection( + renderedExtent, layerState.extent); + } + + if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && + !ol.extent.isEmpty(renderedExtent)) { + var projection = viewState.projection; + if (!ol.ENABLE_RASTER_REPROJECTION) { + var sourceProjection = imageSource.getProjection(); + if (sourceProjection) { + goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection), + 'projection and sourceProjection are equivalent'); + projection = sourceProjection; + } + } + image = imageSource.getImage( + renderedExtent, viewResolution, pixelRatio, projection); + if (image) { + var loaded = this.loadImage(image); + if (loaded) { + this.image_ = image; + } + } + } + + if (this.image_) { + image = this.image_; + var imageExtent = image.getExtent(); + var imageResolution = image.getResolution(); + var imagePixelRatio = image.getPixelRatio(); + var scale = pixelRatio * imageResolution / + (viewResolution * imagePixelRatio); + ol.vec.Mat4.makeTransform2D(this.imageTransform_, + pixelRatio * frameState.size[0] / 2, + pixelRatio * frameState.size[1] / 2, + scale, scale, + 0, + imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, + imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); + this.imageTransformInv_ = null; + this.updateAttributions(frameState.attributions, image.getAttributions()); + this.updateLogos(frameState, imageSource); + } + + return !!this.image_; +}; + +// FIXME find correct globalCompositeOperation + +goog.provide('ol.renderer.canvas.TileLayer'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol.TileRange'); +goog.require('ol.TileState'); +goog.require('ol.array'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.render.EventType'); +goog.require('ol.renderer.canvas.Layer'); +goog.require('ol.size'); +goog.require('ol.source.Tile'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.canvas.Layer} + * @param {ol.layer.Tile|ol.layer.VectorTile} tileLayer Tile layer. + */ +ol.renderer.canvas.TileLayer = function(tileLayer) { + + ol.renderer.canvas.Layer.call(this, tileLayer); + + /** + * @protected + * @type {CanvasRenderingContext2D} + */ + this.context = ol.dom.createCanvasContext2D(); + + /** + * @protected + * @type {Array.<ol.Tile|undefined>} + */ + this.renderedTiles = null; + + /** + * @protected + * @type {ol.Extent} + */ + this.tmpExtent = ol.extent.createEmpty(); + + /** + * @private + * @type {ol.TileCoord} + */ + this.tmpTileCoord_ = [0, 0, 0]; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.imageTransform_ = goog.vec.Mat4.createNumber(); + + /** + * @protected + * @type {number} + */ + this.zDirection = 0; + +}; +ol.inherits(ol.renderer.canvas.TileLayer, ol.renderer.canvas.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.canvas.TileLayer.prototype.composeFrame = function( + frameState, layerState, context) { + var transform = this.getTransform(frameState, 0); + this.dispatchPreComposeEvent(context, frameState, transform); + this.renderTileImages(context, frameState, layerState); + this.dispatchPostComposeEvent(context, frameState, transform); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.TileLayer.prototype.prepareFrame = function( + frameState, layerState) { + + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var projection = viewState.projection; + + var tileLayer = this.getLayer(); + var tileSource = tileLayer.getSource(); + goog.asserts.assertInstanceof(tileSource, ol.source.Tile, + 'source is an ol.source.Tile'); + var tileGrid = tileSource.getTileGridForProjection(projection); + var z = tileGrid.getZForResolution(viewState.resolution, this.zDirection); + var tileResolution = tileGrid.getResolution(z); + var center = viewState.center; + var extent; + if (tileResolution == viewState.resolution) { + center = this.snapCenterToPixel(center, tileResolution, frameState.size); + extent = ol.extent.getForViewAndSize( + center, tileResolution, viewState.rotation, frameState.size); + } else { + extent = frameState.extent; + } + + if (layerState.extent !== undefined) { + extent = ol.extent.getIntersection(extent, layerState.extent); + } + if (ol.extent.isEmpty(extent)) { + // Return false to prevent the rendering of the layer. + return false; + } + + var tileRange = tileGrid.getTileRangeForExtentAndResolution( + extent, tileResolution); + + /** + * @type {Object.<number, Object.<string, ol.Tile>>} + */ + var tilesToDrawByZ = {}; + tilesToDrawByZ[z] = {}; + + var findLoadedTiles = this.createLoadedTileFinder( + tileSource, projection, tilesToDrawByZ); + + var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError(); + + var tmpExtent = ol.extent.createEmpty(); + var tmpTileRange = new ol.TileRange(0, 0, 0, 0); + var childTileRange, fullyLoaded, tile, x, y; + var drawableTile = ( + /** + * @param {!ol.Tile} tile Tile. + * @return {boolean} Tile is selected. + */ + function(tile) { + var tileState = tile.getState(); + return tileState == ol.TileState.LOADED || + tileState == ol.TileState.EMPTY || + tileState == ol.TileState.ERROR && !useInterimTilesOnError; + }); + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + tile = tileSource.getTile(z, x, y, pixelRatio, projection); + if (!drawableTile(tile) && tile.interimTile) { + tile = tile.interimTile; + } + goog.asserts.assert(tile); + if (drawableTile(tile)) { + tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; + continue; + } + fullyLoaded = tileGrid.forEachTileCoordParentTileRange( + tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); + if (!fullyLoaded) { + childTileRange = tileGrid.getTileCoordChildTileRange( + tile.tileCoord, tmpTileRange, tmpExtent); + if (childTileRange) { + findLoadedTiles(z + 1, childTileRange); + } + } + + } + } + + /** @type {Array.<number>} */ + var zs = Object.keys(tilesToDrawByZ).map(Number); + zs.sort(ol.array.numberSafeCompareFunction); + var renderables = []; + var i, ii, currentZ, tileCoordKey, tilesToDraw; + for (i = 0, ii = zs.length; i < ii; ++i) { + currentZ = zs[i]; + tilesToDraw = tilesToDrawByZ[currentZ]; + for (tileCoordKey in tilesToDraw) { + tile = tilesToDraw[tileCoordKey]; + if (tile.getState() == ol.TileState.LOADED) { + renderables.push(tile); + } + } + } + this.renderedTiles = renderables; + + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, + projection, extent, z, tileLayer.getPreload()); + this.scheduleExpireCache(frameState, tileSource); + this.updateLogos(frameState, tileSource); + + return true; +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.TileLayer.prototype.forEachLayerAtPixel = function( + pixel, frameState, callback, thisArg) { + var canvas = this.context.canvas; + var size = frameState.size; + canvas.width = size[0]; + canvas.height = size[1]; + this.composeFrame(frameState, this.getLayer().getLayerState(), this.context); + + var imageData = this.context.getImageData( + pixel[0], pixel[1], 1, 1).data; + + if (imageData[3] > 0) { + return callback.call(thisArg, this.getLayer()); + } else { + return undefined; + } +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @protected + */ +ol.renderer.canvas.TileLayer.prototype.renderTileImages = function(context, frameState, layerState) { + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var center = viewState.center; + var projection = viewState.projection; + var resolution = viewState.resolution; + var rotation = viewState.rotation; + var size = frameState.size; + var offsetX = Math.round(pixelRatio * size[0] / 2); + var offsetY = Math.round(pixelRatio * size[1] / 2); + var pixelScale = pixelRatio / resolution; + var layer = this.getLayer(); + var source = layer.getSource(); + goog.asserts.assertInstanceof(source, ol.source.Tile, + 'source is an ol.source.Tile'); + var tileGutter = source.getGutter(projection); + var tileGrid = source.getTileGridForProjection(projection); + + var hasRenderListeners = layer.hasListener(ol.render.EventType.RENDER); + var renderContext = context; + var drawOffsetX, drawOffsetY, drawScale, drawSize; + if (rotation || hasRenderListeners) { + renderContext = this.context; + var renderCanvas = renderContext.canvas; + var drawZ = tileGrid.getZForResolution(resolution); + var drawTileSize = source.getTilePixelSize(drawZ, pixelRatio, projection); + var tileSize = ol.size.toSize(tileGrid.getTileSize(drawZ)); + drawScale = drawTileSize[0] / tileSize[0]; + var width = context.canvas.width * drawScale; + var height = context.canvas.height * drawScale; + // Make sure the canvas is big enough for all possible rotation angles + drawSize = Math.round(Math.sqrt(width * width + height * height)); + if (renderCanvas.width != drawSize) { + renderCanvas.width = renderCanvas.height = drawSize; + } else { + renderContext.clearRect(0, 0, drawSize, drawSize); + } + drawOffsetX = (drawSize - width) / 2 / drawScale; + drawOffsetY = (drawSize - height) / 2 / drawScale; + pixelScale *= drawScale; + offsetX = Math.round(drawScale * (offsetX + drawOffsetX)); + offsetY = Math.round(drawScale * (offsetY + drawOffsetY)); + } + // for performance reasons, context.save / context.restore is not used + // to save and restore the transformation matrix and the opacity. + // see http://jsperf.com/context-save-restore-versus-variable + var alpha = renderContext.globalAlpha; + renderContext.globalAlpha = layerState.opacity; + + var tilesToDraw = this.renderedTiles; + + var pixelExtents; + var opaque = source.getOpaque(projection) && layerState.opacity == 1; + if (!opaque) { + tilesToDraw.reverse(); + pixelExtents = []; + } + + var extent = layerState.extent; + var clipped = extent !== undefined; + if (clipped) { + goog.asserts.assert(extent !== undefined, + 'layerState extent is defined'); + var topLeft = ol.extent.getTopLeft(extent); + var topRight = ol.extent.getTopRight(extent); + var bottomRight = ol.extent.getBottomRight(extent); + var bottomLeft = ol.extent.getBottomLeft(extent); + + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + topLeft, topLeft); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + topRight, topRight); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + bottomRight, bottomRight); + ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, + bottomLeft, bottomLeft); + + var ox = drawOffsetX || 0; + var oy = drawOffsetY || 0; + renderContext.save(); + var cx = (renderContext.canvas.width * pixelRatio) / 2; + var cy = (renderContext.canvas.height * pixelRatio) / 2; + ol.render.canvas.rotateAtOffset(renderContext, -rotation, cx, cy); + renderContext.beginPath(); + renderContext.moveTo(topLeft[0] * pixelRatio + ox, topLeft[1] * pixelRatio + oy); + renderContext.lineTo(topRight[0] * pixelRatio + ox, topRight[1] * pixelRatio + oy); + renderContext.lineTo(bottomRight[0] * pixelRatio + ox, bottomRight[1] * pixelRatio + oy); + renderContext.lineTo(bottomLeft[0] * pixelRatio + ox, bottomLeft[1] * pixelRatio + oy); + renderContext.clip(); + ol.render.canvas.rotateAtOffset(renderContext, rotation, cx, cy); + } + + for (var i = 0, ii = tilesToDraw.length; i < ii; ++i) { + var tile = tilesToDraw[i]; + var tileCoord = tile.getTileCoord(); + var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent); + var currentZ = tileCoord[0]; + // Calculate all insert points by tile widths from a common origin to avoid + // gaps caused by rounding + var origin = ol.extent.getBottomLeft(tileGrid.getTileCoordExtent( + tileGrid.getTileCoordForCoordAndZ(center, currentZ, this.tmpTileCoord_))); + var w = Math.round(ol.extent.getWidth(tileExtent) * pixelScale); + var h = Math.round(ol.extent.getHeight(tileExtent) * pixelScale); + var left = Math.round((tileExtent[0] - origin[0]) * pixelScale / w) * w + + offsetX + Math.round((origin[0] - center[0]) * pixelScale); + var top = Math.round((origin[1] - tileExtent[3]) * pixelScale / h) * h + + offsetY + Math.round((center[1] - origin[1]) * pixelScale); + if (!opaque) { + var pixelExtent = [left, top, left + w, top + h]; + // Create a clip mask for regions in this low resolution tile that are + // already filled by a higher resolution tile + renderContext.save(); + for (var j = 0, jj = pixelExtents.length; j < jj; ++j) { + var clipExtent = pixelExtents[j]; + if (ol.extent.intersects(pixelExtent, clipExtent)) { + renderContext.beginPath(); + // counter-clockwise (outer ring) for current tile + renderContext.moveTo(pixelExtent[0], pixelExtent[1]); + renderContext.lineTo(pixelExtent[0], pixelExtent[3]); + renderContext.lineTo(pixelExtent[2], pixelExtent[3]); + renderContext.lineTo(pixelExtent[2], pixelExtent[1]); + // clockwise (inner ring) for higher resolution tile + renderContext.moveTo(clipExtent[0], clipExtent[1]); + renderContext.lineTo(clipExtent[2], clipExtent[1]); + renderContext.lineTo(clipExtent[2], clipExtent[3]); + renderContext.lineTo(clipExtent[0], clipExtent[3]); + renderContext.closePath(); + renderContext.clip(); + } + } + pixelExtents.push(pixelExtent); + } + var tilePixelSize = source.getTilePixelSize(currentZ, pixelRatio, projection); + renderContext.drawImage(tile.getImage(), tileGutter, tileGutter, + tilePixelSize[0], tilePixelSize[1], left, top, w, h); + if (!opaque) { + renderContext.restore(); + } + } + + if (clipped) { + renderContext.restore(); + } + + if (hasRenderListeners) { + var dX = drawOffsetX - offsetX / drawScale + offsetX; + var dY = drawOffsetY - offsetY / drawScale + offsetY; + var imageTransform = ol.vec.Mat4.makeTransform2D(this.imageTransform_, + drawSize / 2 - dX, drawSize / 2 - dY, pixelScale, -pixelScale, + -rotation, -center[0] + dX / pixelScale, -center[1] - dY / pixelScale); + this.dispatchRenderEvent(renderContext, frameState, imageTransform); + } + if (rotation || hasRenderListeners) { + context.drawImage(renderContext.canvas, -Math.round(drawOffsetX), + -Math.round(drawOffsetY), drawSize / drawScale, drawSize / drawScale); + } + renderContext.globalAlpha = alpha; +}; + + +/** + * @function + * @return {ol.layer.Tile|ol.layer.VectorTile} + */ +ol.renderer.canvas.TileLayer.prototype.getLayer; + +goog.provide('ol.renderer.canvas.VectorLayer'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.ViewHint'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.layer.Vector'); +goog.require('ol.render.EventType'); +goog.require('ol.render.canvas'); +goog.require('ol.render.canvas.ReplayGroup'); +goog.require('ol.renderer.canvas.Layer'); +goog.require('ol.renderer.vector'); +goog.require('ol.source.Vector'); + + +/** + * @constructor + * @extends {ol.renderer.canvas.Layer} + * @param {ol.layer.Vector} vectorLayer Vector layer. + */ +ol.renderer.canvas.VectorLayer = function(vectorLayer) { + + ol.renderer.canvas.Layer.call(this, vectorLayer); + + /** + * @private + * @type {boolean} + */ + this.dirty_ = false; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.renderedResolution_ = NaN; + + /** + * @private + * @type {ol.Extent} + */ + this.renderedExtent_ = ol.extent.createEmpty(); + + /** + * @private + * @type {function(ol.Feature, ol.Feature): number|null} + */ + this.renderedRenderOrder_ = null; + + /** + * @private + * @type {ol.render.canvas.ReplayGroup} + */ + this.replayGroup_ = null; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = ol.dom.createCanvasContext2D(); + +}; +ol.inherits(ol.renderer.canvas.VectorLayer, ol.renderer.canvas.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) { + + var extent = frameState.extent; + var pixelRatio = frameState.pixelRatio; + var skippedFeatureUids = layerState.managed ? + frameState.skippedFeatureUids : {}; + var viewState = frameState.viewState; + var projection = viewState.projection; + var rotation = viewState.rotation; + var projectionExtent = projection.getExtent(); + var vectorSource = this.getLayer().getSource(); + goog.asserts.assertInstanceof(vectorSource, ol.source.Vector); + + var transform = this.getTransform(frameState, 0); + + this.dispatchPreComposeEvent(context, frameState, transform); + + var replayGroup = this.replayGroup_; + if (replayGroup && !replayGroup.isEmpty()) { + var layer = this.getLayer(); + var replayContext; + if (layer.hasListener(ol.render.EventType.RENDER)) { + // resize and clear + this.context_.canvas.width = context.canvas.width; + this.context_.canvas.height = context.canvas.height; + replayContext = this.context_; + } else { + replayContext = context; + } + // for performance reasons, context.save / context.restore is not used + // to save and restore the transformation matrix and the opacity. + // see http://jsperf.com/context-save-restore-versus-variable + var alpha = replayContext.globalAlpha; + replayContext.globalAlpha = layerState.opacity; + + var width = frameState.size[0] * pixelRatio; + var height = frameState.size[1] * pixelRatio; + ol.render.canvas.rotateAtOffset(replayContext, -rotation, + width / 2, height / 2); + replayGroup.replay(replayContext, pixelRatio, transform, rotation, + skippedFeatureUids); + if (vectorSource.getWrapX() && projection.canWrapX() && + !ol.extent.containsExtent(projectionExtent, extent)) { + var startX = extent[0]; + var worldWidth = ol.extent.getWidth(projectionExtent); + var world = 0; + var offsetX; + while (startX < projectionExtent[0]) { + --world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(replayContext, pixelRatio, transform, rotation, + skippedFeatureUids); + startX += worldWidth; + } + world = 0; + startX = extent[2]; + while (startX > projectionExtent[2]) { + ++world; + offsetX = worldWidth * world; + transform = this.getTransform(frameState, offsetX); + replayGroup.replay(replayContext, pixelRatio, transform, rotation, + skippedFeatureUids); + startX -= worldWidth; + } + // restore original transform for render and compose events + transform = this.getTransform(frameState, 0); + } + ol.render.canvas.rotateAtOffset(replayContext, rotation, + width / 2, height / 2); + + if (replayContext != context) { + this.dispatchRenderEvent(replayContext, frameState, transform); + context.drawImage(replayContext.canvas, 0, 0); + } + replayContext.globalAlpha = alpha; + } + + this.dispatchPostComposeEvent(context, frameState, transform); + +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg) { + if (!this.replayGroup_) { + return undefined; + } else { + var resolution = frameState.viewState.resolution; + var rotation = frameState.viewState.rotation; + var layer = this.getLayer(); + /** @type {Object.<string, boolean>} */ + var features = {}; + return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, + rotation, {}, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + goog.asserts.assert(feature !== undefined, 'received a feature'); + var key = goog.getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, layer); + } + }); + } +}; + + +/** + * Handle changes in image style state. + * @param {ol.events.Event} event Image style change event. + * @private + */ +ol.renderer.canvas.VectorLayer.prototype.handleStyleImageChange_ = function(event) { + this.renderIfReadyAndVisible(); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, layerState) { + + var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); + goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector, + 'layer is an instance of ol.layer.Vector'); + var vectorSource = vectorLayer.getSource(); + + this.updateAttributions( + frameState.attributions, vectorSource.getAttributions()); + this.updateLogos(frameState, vectorSource); + + var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; + var interacting = frameState.viewHints[ol.ViewHint.INTERACTING]; + var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); + var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); + + if (!this.dirty_ && (!updateWhileAnimating && animating) || + (!updateWhileInteracting && interacting)) { + return true; + } + + var frameStateExtent = frameState.extent; + var viewState = frameState.viewState; + var projection = viewState.projection; + var resolution = viewState.resolution; + var pixelRatio = frameState.pixelRatio; + var vectorLayerRevision = vectorLayer.getRevision(); + var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); + var vectorLayerRenderOrder = vectorLayer.getRenderOrder(); + + if (vectorLayerRenderOrder === undefined) { + vectorLayerRenderOrder = ol.renderer.vector.defaultOrder; + } + + var extent = ol.extent.buffer(frameStateExtent, + vectorLayerRenderBuffer * resolution); + var projectionExtent = viewState.projection.getExtent(); + + if (vectorSource.getWrapX() && viewState.projection.canWrapX() && + !ol.extent.containsExtent(projectionExtent, frameState.extent)) { + // For the replay group, we need an extent that intersects the real world + // (-180° to +180°). To support geometries in a coordinate range from -540° + // to +540°, we add at least 1 world width on each side of the projection + // extent. If the viewport is wider than the world, we need to add half of + // the viewport width to make sure we cover the whole viewport. + var worldWidth = ol.extent.getWidth(projectionExtent); + var buffer = Math.max(ol.extent.getWidth(extent) / 2, worldWidth); + extent[0] = projectionExtent[0] - buffer; + extent[2] = projectionExtent[2] + buffer; + } + + if (!this.dirty_ && + this.renderedResolution_ == resolution && + this.renderedRevision_ == vectorLayerRevision && + this.renderedRenderOrder_ == vectorLayerRenderOrder && + ol.extent.containsExtent(this.renderedExtent_, extent)) { + return true; + } + + this.replayGroup_ = null; + + this.dirty_ = false; + + var replayGroup = + new ol.render.canvas.ReplayGroup( + ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, + resolution, vectorLayer.getRenderBuffer()); + vectorSource.loadFeatures(extent, resolution, projection); + /** + * @param {ol.Feature} feature Feature. + * @this {ol.renderer.canvas.VectorLayer} + */ + var renderFeature = function(feature) { + var styles; + var styleFunction = feature.getStyleFunction(); + if (styleFunction) { + styles = styleFunction.call(feature, resolution); + } else { + styleFunction = vectorLayer.getStyleFunction(); + if (styleFunction) { + styles = styleFunction(feature, resolution); + } + } + if (styles) { + var dirty = this.renderFeature( + feature, resolution, pixelRatio, styles, replayGroup); + this.dirty_ = this.dirty_ || dirty; + } + }; + if (vectorLayerRenderOrder) { + /** @type {Array.<ol.Feature>} */ + var features = []; + vectorSource.forEachFeatureInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + features.push(feature); + }, this); + features.sort(vectorLayerRenderOrder); + features.forEach(renderFeature, this); + } else { + vectorSource.forEachFeatureInExtent(extent, renderFeature, this); + } + replayGroup.finish(); + + this.renderedResolution_ = resolution; + this.renderedRevision_ = vectorLayerRevision; + this.renderedRenderOrder_ = vectorLayerRenderOrder; + this.renderedExtent_ = extent; + this.replayGroup_ = replayGroup; + + return true; +}; + + +/** + * @param {ol.Feature} feature Feature. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of + * styles. + * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. + */ +ol.renderer.canvas.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { + if (!styles) { + return false; + } + var loading = false; + if (Array.isArray(styles)) { + for (var i = 0, ii = styles.length; i < ii; ++i) { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles[i], + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + } else { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles, + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + return loading; +}; + +goog.provide('ol.TileUrlFunction'); + +goog.require('goog.asserts'); +goog.require('ol.math'); +goog.require('ol.tilecoord'); + + +/** + * @param {string} template Template. + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @return {ol.TileUrlFunctionType} Tile URL function. + */ +ol.TileUrlFunction.createFromTemplate = function(template, tileGrid) { + var zRegEx = /\{z\}/g; + var xRegEx = /\{x\}/g; + var yRegEx = /\{y\}/g; + var dashYRegEx = /\{-y\}/g; + return ( + /** + * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; + } else { + return template.replace(zRegEx, tileCoord[0].toString()) + .replace(xRegEx, tileCoord[1].toString()) + .replace(yRegEx, function() { + var y = -tileCoord[2] - 1; + return y.toString(); + }) + .replace(dashYRegEx, function() { + var z = tileCoord[0]; + var range = tileGrid.getFullTileRange(z); + goog.asserts.assert(range, + 'The {-y} template requires a tile grid with extent'); + var y = range.getHeight() + tileCoord[2]; + return y.toString(); + }); + } + }); +}; + + +/** + * @param {Array.<string>} templates Templates. + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @return {ol.TileUrlFunctionType} Tile URL function. + */ +ol.TileUrlFunction.createFromTemplates = function(templates, tileGrid) { + var len = templates.length; + var tileUrlFunctions = new Array(len); + for (var i = 0; i < len; ++i) { + tileUrlFunctions[i] = ol.TileUrlFunction.createFromTemplate( + templates[i], tileGrid); + } + return ol.TileUrlFunction.createFromTileUrlFunctions(tileUrlFunctions); +}; + + +/** + * @param {Array.<ol.TileUrlFunctionType>} tileUrlFunctions Tile URL Functions. + * @return {ol.TileUrlFunctionType} Tile URL function. + */ +ol.TileUrlFunction.createFromTileUrlFunctions = function(tileUrlFunctions) { + goog.asserts.assert(tileUrlFunctions.length > 0, + 'Length of tile url functions should be greater than 0'); + if (tileUrlFunctions.length === 1) { + return tileUrlFunctions[0]; + } + return ( + /** + * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; + } else { + var h = ol.tilecoord.hash(tileCoord); + var index = ol.math.modulo(h, tileUrlFunctions.length); + return tileUrlFunctions[index](tileCoord, pixelRatio, projection); + } + }); +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ +ol.TileUrlFunction.nullTileUrlFunction = function(tileCoord, pixelRatio, projection) { + return undefined; +}; + + +/** + * @param {string} url URL. + * @return {Array.<string>} Array of urls. + */ +ol.TileUrlFunction.expandUrl = function(url) { + var urls = []; + var match = /\{(\d)-(\d)\}/.exec(url) || /\{([a-z])-([a-z])\}/.exec(url); + if (match) { + var startCharCode = match[1].charCodeAt(0); + var stopCharCode = match[2].charCodeAt(0); + var charCode; + for (charCode = startCharCode; charCode <= stopCharCode; ++charCode) { + urls.push(url.replace(match[0], String.fromCharCode(charCode))); + } + } else { + urls.push(url); + } + return urls; +}; + +goog.provide('ol.source.UrlTile'); + +goog.require('ol.events'); +goog.require('ol.TileState'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.source.Tile'); +goog.require('ol.source.TileEvent'); + + +/** + * @classdesc + * Base class for sources providing tiles divided into a tile grid over http. + * + * @constructor + * @fires ol.source.TileEvent + * @extends {ol.source.Tile} + * @param {ol.SourceUrlTileOptions} options Image tile options. + */ +ol.source.UrlTile = function(options) { + + ol.source.Tile.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + extent: options.extent, + logo: options.logo, + opaque: options.opaque, + projection: options.projection, + state: options.state, + tileGrid: options.tileGrid, + tilePixelRatio: options.tilePixelRatio, + wrapX: options.wrapX + }); + + /** + * @protected + * @type {ol.TileLoadFunctionType} + */ + this.tileLoadFunction = options.tileLoadFunction; + + /** + * @protected + * @type {ol.TileUrlFunctionType} + */ + this.tileUrlFunction = this.fixedTileUrlFunction ? + this.fixedTileUrlFunction.bind(this) : + ol.TileUrlFunction.nullTileUrlFunction; + + /** + * @protected + * @type {!Array.<string>|null} + */ + this.urls = null; + + if (options.urls) { + this.setUrls(options.urls); + } else if (options.url) { + this.setUrl(options.url); + } + if (options.tileUrlFunction) { + this.setTileUrlFunction(options.tileUrlFunction); + } + +}; +ol.inherits(ol.source.UrlTile, ol.source.Tile); + + +/** + * @type {ol.TileUrlFunctionType|undefined} + * @protected + */ +ol.source.UrlTile.prototype.fixedTileUrlFunction; + +/** + * Return the tile load function of the source. + * @return {ol.TileLoadFunctionType} TileLoadFunction + * @api + */ +ol.source.UrlTile.prototype.getTileLoadFunction = function() { + return this.tileLoadFunction; +}; + + +/** + * Return the tile URL function of the source. + * @return {ol.TileUrlFunctionType} TileUrlFunction + * @api + */ +ol.source.UrlTile.prototype.getTileUrlFunction = function() { + return this.tileUrlFunction; +}; + + +/** + * Return the URLs used for this source. + * When a tileUrlFunction is used instead of url or urls, + * null will be returned. + * @return {!Array.<string>|null} URLs. + * @api + */ +ol.source.UrlTile.prototype.getUrls = function() { + return this.urls; +}; + + +/** + * Handle tile change events. + * @param {ol.events.Event} event Event. + * @protected + */ +ol.source.UrlTile.prototype.handleTileChange = function(event) { + var tile = /** @type {ol.Tile} */ (event.target); + switch (tile.getState()) { + case ol.TileState.LOADING: + this.dispatchEvent( + new ol.source.TileEvent(ol.source.TileEventType.TILELOADSTART, tile)); + break; + case ol.TileState.LOADED: + this.dispatchEvent( + new ol.source.TileEvent(ol.source.TileEventType.TILELOADEND, tile)); + break; + case ol.TileState.ERROR: + this.dispatchEvent( + new ol.source.TileEvent(ol.source.TileEventType.TILELOADERROR, tile)); + break; + default: + // pass + } +}; + + +/** + * Set the tile load function of the source. + * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. + * @api + */ +ol.source.UrlTile.prototype.setTileLoadFunction = function(tileLoadFunction) { + this.tileCache.clear(); + this.tileLoadFunction = tileLoadFunction; + this.changed(); +}; + + +/** + * Set the tile URL function of the source. + * @param {ol.TileUrlFunctionType} tileUrlFunction Tile URL function. + * @param {string=} opt_key Optional new tile key for the source. + * @api + */ +ol.source.UrlTile.prototype.setTileUrlFunction = function(tileUrlFunction, opt_key) { + this.tileUrlFunction = tileUrlFunction; + if (typeof opt_key !== 'undefined') { + this.setKey(opt_key); + } else { + this.changed(); + } +}; + + +/** + * Set the URL to use for requests. + * @param {string} url URL. + * @api stable + */ +ol.source.UrlTile.prototype.setUrl = function(url) { + var urls = this.urls = ol.TileUrlFunction.expandUrl(url); + this.setTileUrlFunction(this.fixedTileUrlFunction ? + this.fixedTileUrlFunction.bind(this) : + ol.TileUrlFunction.createFromTemplates(urls, this.tileGrid), url); +}; + + +/** + * Set the URLs to use for requests. + * @param {Array.<string>} urls URLs. + * @api stable + */ +ol.source.UrlTile.prototype.setUrls = function(urls) { + this.urls = urls; + var key = urls.join('\n'); + this.setTileUrlFunction(this.fixedTileUrlFunction ? + this.fixedTileUrlFunction.bind(this) : + ol.TileUrlFunction.createFromTemplates(urls, this.tileGrid), key); +}; + + +/** + * @inheritDoc + */ +ol.source.UrlTile.prototype.useTile = function(z, x, y) { + var tileCoordKey = this.getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + this.tileCache.get(tileCoordKey); + } +}; + +goog.provide('ol.source.VectorTile'); + +goog.require('ol.TileState'); +goog.require('ol.VectorTile'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.featureloader'); +goog.require('ol.size'); +goog.require('ol.source.UrlTile'); + + +/** + * @classdesc + * Class for layer sources providing vector data divided into a tile grid, to be + * used with {@link ol.layer.VectorTile}. Although this source receives tiles + * with vector features from the server, it is not meant for feature editing. + * Features are optimized for rendering, their geometries are clipped at or near + * tile boundaries and simplified for a view resolution. See + * {@link ol.source.Vector} for vector sources that are suitable for feature + * editing. + * + * @constructor + * @fires ol.source.TileEvent + * @extends {ol.source.UrlTile} + * @param {olx.source.VectorTileOptions} options Vector tile options. + * @api + */ +ol.source.VectorTile = function(options) { + + ol.source.UrlTile.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize !== undefined ? options.cacheSize : 128, + extent: options.extent, + logo: options.logo, + opaque: false, + projection: options.projection, + state: options.state, + tileGrid: options.tileGrid, + tileLoadFunction: options.tileLoadFunction ? + options.tileLoadFunction : ol.source.VectorTile.defaultTileLoadFunction, + tileUrlFunction: options.tileUrlFunction, + tilePixelRatio: options.tilePixelRatio, + url: options.url, + urls: options.urls, + wrapX: options.wrapX === undefined ? true : options.wrapX + }); + + /** + * @private + * @type {ol.format.Feature} + */ + this.format_ = options.format ? options.format : null; + + /** + * @protected + * @type {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string, + * ol.format.Feature, ol.TileLoadFunctionType)} + */ + this.tileClass = options.tileClass ? options.tileClass : ol.VectorTile; + +}; +ol.inherits(ol.source.VectorTile, ol.source.UrlTile); + + +/** + * @inheritDoc + */ +ol.source.VectorTile.prototype.getTile = function(z, x, y, pixelRatio, projection) { + var tileCoordKey = this.getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); + } else { + var tileCoord = [z, x, y]; + var urlTileCoord = this.getTileCoordForTileUrlFunction( + tileCoord, projection); + var tileUrl = urlTileCoord ? + this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined; + var tile = new this.tileClass( + tileCoord, + tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, + tileUrl !== undefined ? tileUrl : '', + this.format_, this.tileLoadFunction); + ol.events.listen(tile, ol.events.EventType.CHANGE, + this.handleTileChange, this); + + this.tileCache.set(tileCoordKey, tile); + return tile; + } +}; + + +/** + * @inheritDoc + */ +ol.source.VectorTile.prototype.getTilePixelSize = function(z, pixelRatio, projection) { + var tileSize = ol.size.toSize(this.tileGrid.getTileSize(z)); + return [tileSize[0] * pixelRatio, tileSize[1] * pixelRatio]; +}; + + +/** + * @param {ol.VectorTile} vectorTile Vector tile. + * @param {string} url URL. + */ +ol.source.VectorTile.defaultTileLoadFunction = function(vectorTile, url) { + vectorTile.setLoader(ol.featureloader.tile(url, vectorTile.getFormat())); +}; + +goog.provide('ol.renderer.canvas.VectorTileLayer'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('goog.vec.Mat4'); +goog.require('ol.Feature'); +goog.require('ol.VectorTile'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.layer.VectorTile'); +goog.require('ol.proj'); +goog.require('ol.proj.Units'); +goog.require('ol.render.EventType'); +goog.require('ol.render.canvas'); +goog.require('ol.render.canvas.ReplayGroup'); +goog.require('ol.renderer.canvas.TileLayer'); +goog.require('ol.renderer.vector'); +goog.require('ol.size'); +goog.require('ol.source.VectorTile'); +goog.require('ol.vec.Mat4'); + + +/** + * @const + * @type {!Object.<string, Array.<ol.render.ReplayType>>} + */ +ol.renderer.canvas.IMAGE_REPLAYS = { + 'image': ol.render.REPLAY_ORDER, + 'hybrid': [ol.render.ReplayType.POLYGON, ol.render.ReplayType.LINE_STRING] +}; + + +/** + * @const + * @type {!Object.<string, Array.<ol.render.ReplayType>>} + */ +ol.renderer.canvas.VECTOR_REPLAYS = { + 'hybrid': [ol.render.ReplayType.IMAGE, ol.render.ReplayType.TEXT], + 'vector': ol.render.REPLAY_ORDER +}; + + +/** + * @constructor + * @extends {ol.renderer.canvas.TileLayer} + * @param {ol.layer.VectorTile} layer VectorTile layer. + */ +ol.renderer.canvas.VectorTileLayer = function(layer) { + + ol.renderer.canvas.TileLayer.call(this, layer); + + /** + * @private + * @type {boolean} + */ + this.dirty_ = false; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.tmpTransform_ = goog.vec.Mat4.createNumber(); + + // Use lower resolution for pure vector rendering. Closest resolution otherwise. + this.zDirection = + layer.getRenderMode() == ol.layer.VectorTileRenderType.VECTOR ? 1 : 0; + +}; +ol.inherits(ol.renderer.canvas.VectorTileLayer, ol.renderer.canvas.TileLayer); + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorTileLayer.prototype.composeFrame = function( + frameState, layerState, context) { + var transform = this.getTransform(frameState, 0); + this.dispatchPreComposeEvent(context, frameState, transform); + var renderMode = this.getLayer().getRenderMode(); + if (renderMode !== ol.layer.VectorTileRenderType.VECTOR) { + this.renderTileImages(context, frameState, layerState); + } + if (renderMode !== ol.layer.VectorTileRenderType.IMAGE) { + this.renderTileReplays_(context, frameState, layerState); + } + this.dispatchPostComposeEvent(context, frameState, transform); +}; + + +/** + * @param {CanvasRenderingContext2D} context Context. + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @private + */ +ol.renderer.canvas.VectorTileLayer.prototype.renderTileReplays_ = function( + context, frameState, layerState) { + + var layer = this.getLayer(); + var replays = ol.renderer.canvas.VECTOR_REPLAYS[layer.getRenderMode()]; + var pixelRatio = frameState.pixelRatio; + var skippedFeatureUids = layerState.managed ? + frameState.skippedFeatureUids : {}; + var viewState = frameState.viewState; + var center = viewState.center; + var resolution = viewState.resolution; + var rotation = viewState.rotation; + var size = frameState.size; + var pixelScale = pixelRatio / resolution; + var source = layer.getSource(); + goog.asserts.assertInstanceof(source, ol.source.VectorTile, + 'Source is an ol.source.VectorTile'); + var tilePixelRatio = source.getTilePixelRatio(pixelRatio); + + var transform = this.getTransform(frameState, 0); + + var replayContext; + if (layer.hasListener(ol.render.EventType.RENDER)) { + // resize and clear + this.context.canvas.width = context.canvas.width; + this.context.canvas.height = context.canvas.height; + replayContext = this.context; + } else { + replayContext = context; + } + // for performance reasons, context.save / context.restore is not used + // to save and restore the transformation matrix and the opacity. + // see http://jsperf.com/context-save-restore-versus-variable + var alpha = replayContext.globalAlpha; + replayContext.globalAlpha = layerState.opacity; + + var tilesToDraw = this.renderedTiles; + var tileGrid = source.getTileGrid(); + + var currentZ, i, ii, offsetX, offsetY, origin, pixelSpace, replayState; + var tile, tileExtent, tilePixelResolution, tileResolution, tileTransform; + for (i = 0, ii = tilesToDraw.length; i < ii; ++i) { + tile = tilesToDraw[i]; + replayState = tile.getReplayState(); + tileExtent = tileGrid.getTileCoordExtent( + tile.getTileCoord(), this.tmpExtent); + currentZ = tile.getTileCoord()[0]; + pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS; + tileResolution = tileGrid.getResolution(currentZ); + tilePixelResolution = tileResolution / tilePixelRatio; + offsetX = Math.round(pixelRatio * size[0] / 2); + offsetY = Math.round(pixelRatio * size[1] / 2); + + if (pixelSpace) { + origin = ol.extent.getTopLeft(tileExtent); + tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_, + offsetX, offsetY, + pixelScale * tilePixelResolution, + pixelScale * tilePixelResolution, + rotation, + (origin[0] - center[0]) / tilePixelResolution, + (center[1] - origin[1]) / tilePixelResolution); + } else { + tileTransform = transform; + } + ol.render.canvas.rotateAtOffset(replayContext, -rotation, offsetX, offsetY); + replayState.replayGroup.replay(replayContext, pixelRatio, + tileTransform, rotation, skippedFeatureUids, replays); + ol.render.canvas.rotateAtOffset(replayContext, rotation, offsetX, offsetY); + } + + if (replayContext != context) { + this.dispatchRenderEvent(replayContext, frameState, transform); + context.drawImage(replayContext.canvas, 0, 0); + } + replayContext.globalAlpha = alpha; +}; + + +/** + * @param {ol.VectorTile} tile Tile. + * @param {olx.FrameState} frameState Frame state. + */ +ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup = function(tile, + frameState) { + var layer = this.getLayer(); + var pixelRatio = frameState.pixelRatio; + var projection = frameState.viewState.projection; + var revision = layer.getRevision(); + var renderOrder = layer.getRenderOrder() || null; + + var replayState = tile.getReplayState(); + if (!replayState.dirty && replayState.renderedRevision == revision && + replayState.renderedRenderOrder == renderOrder) { + return; + } + + replayState.replayGroup = null; + replayState.dirty = false; + + var source = layer.getSource(); + goog.asserts.assertInstanceof(source, ol.source.VectorTile, + 'Source is an ol.source.VectorTile'); + var tileGrid = source.getTileGrid(); + var tileCoord = tile.getTileCoord(); + var tileProjection = tile.getProjection(); + var pixelSpace = tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS; + var resolution = tileGrid.getResolution(tileCoord[0]); + var extent, reproject, tileResolution; + if (pixelSpace) { + var tilePixelRatio = tileResolution = source.getTilePixelRatio(pixelRatio); + var tileSize = ol.size.toSize(tileGrid.getTileSize(tileCoord[0])); + extent = [0, 0, tileSize[0] * tilePixelRatio, tileSize[1] * tilePixelRatio]; + } else { + tileResolution = resolution; + extent = tileGrid.getTileCoordExtent(tileCoord); + if (!ol.proj.equivalent(projection, tileProjection)) { + reproject = true; + tile.setProjection(projection); + } + } + replayState.dirty = false; + var replayGroup = new ol.render.canvas.ReplayGroup(0, extent, + tileResolution, layer.getRenderBuffer()); + var squaredTolerance = ol.renderer.vector.getSquaredTolerance( + tileResolution, pixelRatio); + + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @this {ol.renderer.canvas.VectorTileLayer} + */ + function renderFeature(feature) { + var styles; + var styleFunction = feature.getStyleFunction(); + if (styleFunction) { + goog.asserts.assertInstanceof(feature, ol.Feature, 'Got an ol.Feature'); + styles = styleFunction.call(feature, resolution); + } else { + styleFunction = layer.getStyleFunction(); + if (styleFunction) { + styles = styleFunction(feature, resolution); + } + } + if (styles) { + if (!Array.isArray(styles)) { + styles = [styles]; + } + var dirty = this.renderFeature(feature, squaredTolerance, styles, + replayGroup); + this.dirty_ = this.dirty_ || dirty; + replayState.dirty = replayState.dirty || dirty; + } + } + + var features = tile.getFeatures(); + if (renderOrder && renderOrder !== replayState.renderedRenderOrder) { + features.sort(renderOrder); + } + var feature; + for (var i = 0, ii = features.length; i < ii; ++i) { + feature = features[i]; + if (reproject) { + feature.getGeometry().transform(tileProjection, projection); + } + renderFeature.call(this, feature); + } + + replayGroup.finish(); + + replayState.renderedRevision = revision; + replayState.renderedRenderOrder = renderOrder; + replayState.replayGroup = replayGroup; + replayState.resolution = NaN; +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorTileLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg) { + var pixelRatio = frameState.pixelRatio; + var resolution = frameState.viewState.resolution; + var rotation = frameState.viewState.rotation; + var layer = this.getLayer(); + /** @type {Object.<string, boolean>} */ + var features = {}; + + var replayables = this.renderedTiles; + var source = layer.getSource(); + goog.asserts.assertInstanceof(source, ol.source.VectorTile, + 'Source is an ol.source.VectorTile'); + var tileGrid = source.getTileGrid(); + var found, tileSpaceCoordinate; + var i, ii, origin, replayGroup; + var tile, tileCoord, tileExtent, tilePixelRatio, tileResolution; + for (i = 0, ii = replayables.length; i < ii; ++i) { + tile = replayables[i]; + tileCoord = tile.getTileCoord(); + tileExtent = source.getTileGrid().getTileCoordExtent(tileCoord, + this.tmpExtent); + if (!ol.extent.containsCoordinate(tileExtent, coordinate)) { + continue; + } + if (tile.getProjection().getUnits() === ol.proj.Units.TILE_PIXELS) { + origin = ol.extent.getTopLeft(tileExtent); + tilePixelRatio = source.getTilePixelRatio(pixelRatio); + tileResolution = tileGrid.getResolution(tileCoord[0]) / tilePixelRatio; + tileSpaceCoordinate = [ + (coordinate[0] - origin[0]) / tileResolution, + (origin[1] - coordinate[1]) / tileResolution + ]; + resolution = tilePixelRatio; + } else { + tileSpaceCoordinate = coordinate; + } + replayGroup = tile.getReplayState().replayGroup; + found = found || replayGroup.forEachFeatureAtCoordinate( + tileSpaceCoordinate, resolution, rotation, {}, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + goog.asserts.assert(feature, 'received a feature'); + var key = goog.getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, layer); + } + }); + } + return found; +}; + + +/** + * Handle changes in image style state. + * @param {ol.events.Event} event Image style change event. + * @private + */ +ol.renderer.canvas.VectorTileLayer.prototype.handleStyleImageChange_ = function(event) { + this.renderIfReadyAndVisible(); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, layerState) { + var prepared = ol.renderer.canvas.TileLayer.prototype.prepareFrame.call(this, frameState, layerState); + if (prepared) { + var skippedFeatures = Object.keys(frameState.skippedFeatureUids_ || {}); + for (var i = 0, ii = this.renderedTiles.length; i < ii; ++i) { + var tile = this.renderedTiles[i]; + goog.asserts.assertInstanceof(tile, ol.VectorTile, 'got an ol.VectorTile'); + this.createReplayGroup(tile, frameState); + this.renderTileImage_(tile, frameState, layerState, skippedFeatures); + } + } + return prepared; +}; + + +/** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {number} squaredTolerance Squared tolerance. + * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of + * styles. + * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. + */ +ol.renderer.canvas.VectorTileLayer.prototype.renderFeature = function(feature, squaredTolerance, styles, replayGroup) { + if (!styles) { + return false; + } + var loading = false; + if (Array.isArray(styles)) { + for (var i = 0, ii = styles.length; i < ii; ++i) { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles[i], squaredTolerance, + this.handleStyleImageChange_, this) || loading; + } + } else { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles, squaredTolerance, + this.handleStyleImageChange_, this) || loading; + } + return loading; +}; + + +/** + * @param {ol.VectorTile} tile Tile. + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @param {Array.<string>} skippedFeatures Skipped features. + * @private + */ +ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function( + tile, frameState, layerState, skippedFeatures) { + var layer = this.getLayer(); + var replays = ol.renderer.canvas.IMAGE_REPLAYS[layer.getRenderMode()]; + if (!replays) { + // do not create an image in 'vector' mode + return; + } + var pixelRatio = frameState.pixelRatio; + var replayState = tile.getReplayState(); + var revision = layer.getRevision(); + if (!ol.array.equals(replayState.skippedFeatures, skippedFeatures) || + replayState.renderedTileRevision !== revision) { + replayState.skippedFeatures = skippedFeatures; + replayState.renderedTileRevision = revision; + var tileContext = tile.getContext(); + var source = layer.getSource(); + var tileGrid = source.getTileGrid(); + var currentZ = tile.getTileCoord()[0]; + var resolution = tileGrid.getResolution(currentZ); + var tileSize = ol.size.toSize(tileGrid.getTileSize(currentZ)); + var tileResolution = tileGrid.getResolution(currentZ); + var resolutionRatio = tileResolution / resolution; + var width = tileSize[0] * pixelRatio * resolutionRatio; + var height = tileSize[1] * pixelRatio * resolutionRatio; + tileContext.canvas.width = width / resolutionRatio + 0.5; + tileContext.canvas.height = height / resolutionRatio + 0.5; + tileContext.scale(1 / resolutionRatio, 1 / resolutionRatio); + tileContext.translate(width / 2, height / 2); + var pixelSpace = tile.getProjection().getUnits() == ol.proj.Units.TILE_PIXELS; + var pixelScale = pixelRatio / resolution; + var tilePixelRatio = source.getTilePixelRatio(pixelRatio); + var tilePixelResolution = tileResolution / tilePixelRatio; + var tileExtent = tileGrid.getTileCoordExtent( + tile.getTileCoord(), this.tmpExtent); + var tileTransform; + if (pixelSpace) { + tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_, + 0, 0, + pixelScale * tilePixelResolution, pixelScale * tilePixelResolution, + 0, + -tileSize[0] * tilePixelRatio / 2, -tileSize[1] * tilePixelRatio / 2); + } else { + var tileCenter = ol.extent.getCenter(tileExtent); + tileTransform = ol.vec.Mat4.makeTransform2D(this.tmpTransform_, + 0, 0, + pixelScale, -pixelScale, + 0, + -tileCenter[0], -tileCenter[1]); + } + + replayState.replayGroup.replay(tileContext, pixelRatio, + tileTransform, 0, frameState.skippedFeatureUids || {}, replays); + } +}; + +// FIXME offset panning + +goog.provide('ol.renderer.canvas.Map'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol'); +goog.require('ol.RendererType'); +goog.require('ol.array'); +goog.require('ol.css'); +goog.require('ol.dom'); +goog.require('ol.layer.Image'); +goog.require('ol.layer.Layer'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.layer.VectorTile'); +goog.require('ol.render.Event'); +goog.require('ol.render.EventType'); +goog.require('ol.render.canvas'); +goog.require('ol.render.canvas.Immediate'); +goog.require('ol.renderer.Map'); +goog.require('ol.renderer.canvas.ImageLayer'); +goog.require('ol.renderer.canvas.Layer'); +goog.require('ol.renderer.canvas.TileLayer'); +goog.require('ol.renderer.canvas.VectorLayer'); +goog.require('ol.renderer.canvas.VectorTileLayer'); +goog.require('ol.source.State'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.Map} + * @param {Element} container Container. + * @param {ol.Map} map Map. + */ +ol.renderer.canvas.Map = function(container, map) { + + ol.renderer.Map.call(this, container, map); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = ol.dom.createCanvasContext2D(); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = this.context_.canvas; + + this.canvas_.style.width = '100%'; + this.canvas_.style.height = '100%'; + this.canvas_.className = ol.css.CLASS_UNSELECTABLE; + container.insertBefore(this.canvas_, container.childNodes[0] || null); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + +}; +ol.inherits(ol.renderer.canvas.Map, ol.renderer.Map); + + +/** + * @inheritDoc + */ +ol.renderer.canvas.Map.prototype.createLayerRenderer = function(layer) { + if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) { + return new ol.renderer.canvas.ImageLayer(layer); + } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) { + return new ol.renderer.canvas.TileLayer(layer); + } else if (ol.ENABLE_VECTOR_TILE && layer instanceof ol.layer.VectorTile) { + return new ol.renderer.canvas.VectorTileLayer(layer); + } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) { + return new ol.renderer.canvas.VectorLayer(layer); + } else { + goog.asserts.fail('unexpected layer configuration'); + return null; + } +}; + + +/** + * @param {ol.render.EventType} type Event type. + * @param {olx.FrameState} frameState Frame state. + * @private + */ +ol.renderer.canvas.Map.prototype.dispatchComposeEvent_ = function(type, frameState) { + var map = this.getMap(); + var context = this.context_; + if (map.hasListener(type)) { + var extent = frameState.extent; + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var rotation = viewState.rotation; + + var transform = this.getTransform(frameState); + + var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio, + extent, transform, rotation); + var composeEvent = new ol.render.Event(type, map, vectorContext, + frameState, context, null); + map.dispatchEvent(composeEvent); + } +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @protected + * @return {!goog.vec.Mat4.Number} Transform. + */ +ol.renderer.canvas.Map.prototype.getTransform = function(frameState) { + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var resolution = viewState.resolution; + return ol.vec.Mat4.makeTransform2D(this.transform_, + this.canvas_.width / 2, this.canvas_.height / 2, + pixelRatio / resolution, -pixelRatio / resolution, + -viewState.rotation, + -viewState.center[0], -viewState.center[1]); +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.Map.prototype.getType = function() { + return ol.RendererType.CANVAS; +}; + + +/** + * @inheritDoc + */ +ol.renderer.canvas.Map.prototype.renderFrame = function(frameState) { + + if (!frameState) { + if (this.renderedVisible_) { + this.canvas_.style.display = 'none'; + this.renderedVisible_ = false; + } + return; + } + + var context = this.context_; + var pixelRatio = frameState.pixelRatio; + var width = Math.round(frameState.size[0] * pixelRatio); + var height = Math.round(frameState.size[1] * pixelRatio); + if (this.canvas_.width != width || this.canvas_.height != height) { + this.canvas_.width = width; + this.canvas_.height = height; + } else { + context.clearRect(0, 0, width, height); + } + + var rotation = frameState.viewState.rotation; + + this.calculateMatrices2D(frameState); + + this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState); + + var layerStatesArray = frameState.layerStatesArray; + ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex); + + ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); + + var viewResolution = frameState.viewState.resolution; + var i, ii, layer, layerRenderer, layerState; + for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerState = layerStatesArray[i]; + layer = layerState.layer; + layerRenderer = this.getLayerRenderer(layer); + goog.asserts.assertInstanceof(layerRenderer, ol.renderer.canvas.Layer, + 'layerRenderer is an instance of ol.renderer.canvas.Layer'); + if (!ol.layer.Layer.visibleAtResolution(layerState, viewResolution) || + layerState.sourceState != ol.source.State.READY) { + continue; + } + if (layerRenderer.prepareFrame(frameState, layerState)) { + layerRenderer.composeFrame(frameState, layerState, context); + } + } + + ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); + + this.dispatchComposeEvent_( + ol.render.EventType.POSTCOMPOSE, frameState); + + if (!this.renderedVisible_) { + this.canvas_.style.display = ''; + this.renderedVisible_ = true; + } + + this.scheduleRemoveUnusedLayerRenderers(frameState); + this.scheduleExpireIconCache(frameState); +}; + +goog.provide('ol.renderer.dom.Layer'); + +goog.require('ol'); +goog.require('ol.layer.Layer'); +goog.require('ol.renderer.Layer'); + + +/** + * @constructor + * @extends {ol.renderer.Layer} + * @param {ol.layer.Layer} layer Layer. + * @param {!Element} target Target. + */ +ol.renderer.dom.Layer = function(layer, target) { + + ol.renderer.Layer.call(this, layer); + + /** + * @type {!Element} + * @protected + */ + this.target = target; + +}; +ol.inherits(ol.renderer.dom.Layer, ol.renderer.Layer); + + +/** + * Clear rendered elements. + */ +ol.renderer.dom.Layer.prototype.clearFrame = ol.nullFunction; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + */ +ol.renderer.dom.Layer.prototype.composeFrame = ol.nullFunction; + + +/** + * @return {!Element} Target. + */ +ol.renderer.dom.Layer.prototype.getTarget = function() { + return this.target; +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @return {boolean} whether composeFrame should be called. + */ +ol.renderer.dom.Layer.prototype.prepareFrame = goog.abstractMethod; + +goog.provide('ol.renderer.dom.ImageLayer'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol.ImageBase'); +goog.require('ol.ViewHint'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.layer.Image'); +goog.require('ol.proj'); +goog.require('ol.renderer.dom.Layer'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.dom.Layer} + * @param {ol.layer.Image} imageLayer Image layer. + */ +ol.renderer.dom.ImageLayer = function(imageLayer) { + var target = document.createElement('DIV'); + target.style.position = 'absolute'; + + ol.renderer.dom.Layer.call(this, imageLayer, target); + + /** + * The last rendered image. + * @private + * @type {?ol.ImageBase} + */ + this.image_ = null; + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumberIdentity(); + +}; +ol.inherits(ol.renderer.dom.ImageLayer, ol.renderer.dom.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.dom.ImageLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg) { + var layer = this.getLayer(); + var source = layer.getSource(); + var resolution = frameState.viewState.resolution; + var rotation = frameState.viewState.rotation; + var skippedFeatureUids = frameState.skippedFeatureUids; + return source.forEachFeatureAtCoordinate( + coordinate, resolution, rotation, skippedFeatureUids, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(thisArg, feature, layer); + }); +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.ImageLayer.prototype.clearFrame = function() { + ol.dom.removeChildren(this.target); + this.image_ = null; +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.ImageLayer.prototype.prepareFrame = function(frameState, layerState) { + + var viewState = frameState.viewState; + var viewCenter = viewState.center; + var viewResolution = viewState.resolution; + var viewRotation = viewState.rotation; + + var image = this.image_; + var imageLayer = this.getLayer(); + goog.asserts.assertInstanceof(imageLayer, ol.layer.Image, + 'layer is an instance of ol.layer.Image'); + var imageSource = imageLayer.getSource(); + + var hints = frameState.viewHints; + + var renderedExtent = frameState.extent; + if (layerState.extent !== undefined) { + renderedExtent = ol.extent.getIntersection( + renderedExtent, layerState.extent); + } + + if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && + !ol.extent.isEmpty(renderedExtent)) { + var projection = viewState.projection; + if (!ol.ENABLE_RASTER_REPROJECTION) { + var sourceProjection = imageSource.getProjection(); + if (sourceProjection) { + goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection), + 'projection and sourceProjection are equivalent'); + projection = sourceProjection; + } + } + var image_ = imageSource.getImage(renderedExtent, viewResolution, + frameState.pixelRatio, projection); + if (image_) { + var loaded = this.loadImage(image_); + if (loaded) { + image = image_; + } + } + } + + if (image) { + var imageExtent = image.getExtent(); + var imageResolution = image.getResolution(); + var transform = goog.vec.Mat4.createNumber(); + ol.vec.Mat4.makeTransform2D(transform, + frameState.size[0] / 2, frameState.size[1] / 2, + imageResolution / viewResolution, imageResolution / viewResolution, + viewRotation, + (imageExtent[0] - viewCenter[0]) / imageResolution, + (viewCenter[1] - imageExtent[3]) / imageResolution); + if (image != this.image_) { + var imageElement = image.getImage(this); + // Bootstrap sets the style max-width: 100% for all images, which breaks + // prevents the image from being displayed in FireFox. Workaround by + // overriding the max-width style. + imageElement.style.maxWidth = 'none'; + imageElement.style.position = 'absolute'; + ol.dom.removeChildren(this.target); + this.target.appendChild(imageElement); + this.image_ = image; + } + this.setTransform_(transform); + this.updateAttributions(frameState.attributions, image.getAttributions()); + this.updateLogos(frameState, imageSource); + } + + return true; +}; + + +/** + * @param {goog.vec.Mat4.Number} transform Transform. + * @private + */ +ol.renderer.dom.ImageLayer.prototype.setTransform_ = function(transform) { + if (!ol.vec.Mat4.equals2D(transform, this.transform_)) { + ol.dom.transformElement2D(this.target, transform, 6); + goog.vec.Mat4.setFromArray(this.transform_, transform); + } +}; + +// FIXME probably need to reset TileLayerZ if offsets get too large +// FIXME when zooming out, preserve higher Z divs to avoid white flash + +goog.provide('ol.renderer.dom.TileLayer'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol'); +goog.require('ol.TileRange'); +goog.require('ol.TileState'); +goog.require('ol.ViewHint'); +goog.require('ol.array'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.layer.Tile'); +goog.require('ol.renderer.dom.Layer'); +goog.require('ol.size'); +goog.require('ol.tilegrid.TileGrid'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.dom.Layer} + * @param {ol.layer.Tile} tileLayer Tile layer. + */ +ol.renderer.dom.TileLayer = function(tileLayer) { + + var target = document.createElement('DIV'); + target.style.position = 'absolute'; + + ol.renderer.dom.Layer.call(this, tileLayer, target); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + + /** + * @private + * @type {number} + */ + this.renderedOpacity_ = 1; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + + /** + * @private + * @type {!Object.<number, ol.renderer.dom.TileLayerZ_>} + */ + this.tileLayerZs_ = {}; + +}; +ol.inherits(ol.renderer.dom.TileLayer, ol.renderer.dom.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.dom.TileLayer.prototype.clearFrame = function() { + ol.dom.removeChildren(this.target); + this.renderedRevision_ = 0; +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.TileLayer.prototype.prepareFrame = function(frameState, layerState) { + + if (!layerState.visible) { + if (this.renderedVisible_) { + this.target.style.display = 'none'; + this.renderedVisible_ = false; + } + return true; + } + + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var projection = viewState.projection; + + var tileLayer = this.getLayer(); + goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile, + 'layer is an instance of ol.layer.Tile'); + var tileSource = tileLayer.getSource(); + var tileGrid = tileSource.getTileGridForProjection(projection); + var tileGutter = tileSource.getGutter(projection); + var z = tileGrid.getZForResolution(viewState.resolution); + var tileResolution = tileGrid.getResolution(z); + var center = viewState.center; + var extent; + if (tileResolution == viewState.resolution) { + center = this.snapCenterToPixel(center, tileResolution, frameState.size); + extent = ol.extent.getForViewAndSize( + center, tileResolution, viewState.rotation, frameState.size); + } else { + extent = frameState.extent; + } + + if (layerState.extent !== undefined) { + extent = ol.extent.getIntersection(extent, layerState.extent); + } + + var tileRange = tileGrid.getTileRangeForExtentAndResolution( + extent, tileResolution); + + /** @type {Object.<number, Object.<string, ol.Tile>>} */ + var tilesToDrawByZ = {}; + tilesToDrawByZ[z] = {}; + + var findLoadedTiles = this.createLoadedTileFinder( + tileSource, projection, tilesToDrawByZ); + + var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError(); + + var tmpExtent = ol.extent.createEmpty(); + var tmpTileRange = new ol.TileRange(0, 0, 0, 0); + var childTileRange, drawable, fullyLoaded, tile, tileState, x, y; + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + tile = tileSource.getTile(z, x, y, pixelRatio, projection); + tileState = tile.getState(); + drawable = tileState == ol.TileState.LOADED || + tileState == ol.TileState.EMPTY || + tileState == ol.TileState.ERROR && !useInterimTilesOnError; + if (!drawable && tile.interimTile) { + tile = tile.interimTile; + } + goog.asserts.assert(tile); + tileState = tile.getState(); + if (tileState == ol.TileState.LOADED) { + tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; + continue; + } else if (tileState == ol.TileState.EMPTY || + (tileState == ol.TileState.ERROR && + !useInterimTilesOnError)) { + continue; + } + fullyLoaded = tileGrid.forEachTileCoordParentTileRange( + tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); + if (!fullyLoaded) { + childTileRange = tileGrid.getTileCoordChildTileRange( + tile.tileCoord, tmpTileRange, tmpExtent); + if (childTileRange) { + findLoadedTiles(z + 1, childTileRange); + } + } + + } + + } + + // If the tile source revision changes, we destroy the existing DOM structure + // so that a new one will be created. It would be more efficient to modify + // the existing structure. + var tileLayerZ, tileLayerZKey; + if (this.renderedRevision_ != tileSource.getRevision()) { + for (tileLayerZKey in this.tileLayerZs_) { + tileLayerZ = this.tileLayerZs_[+tileLayerZKey]; + ol.dom.removeNode(tileLayerZ.target); + } + this.tileLayerZs_ = {}; + this.renderedRevision_ = tileSource.getRevision(); + } + + /** @type {Array.<number>} */ + var zs = Object.keys(tilesToDrawByZ).map(Number); + zs.sort(ol.array.numberSafeCompareFunction); + + /** @type {Object.<number, boolean>} */ + var newTileLayerZKeys = {}; + + var iz, iziz, tileCoordKey, tileCoordOrigin, tilesToDraw; + for (iz = 0, iziz = zs.length; iz < iziz; ++iz) { + tileLayerZKey = zs[iz]; + if (tileLayerZKey in this.tileLayerZs_) { + tileLayerZ = this.tileLayerZs_[tileLayerZKey]; + } else { + tileCoordOrigin = + tileGrid.getTileCoordForCoordAndZ(center, tileLayerZKey); + tileLayerZ = new ol.renderer.dom.TileLayerZ_(tileGrid, tileCoordOrigin); + newTileLayerZKeys[tileLayerZKey] = true; + this.tileLayerZs_[tileLayerZKey] = tileLayerZ; + } + tilesToDraw = tilesToDrawByZ[tileLayerZKey]; + for (tileCoordKey in tilesToDraw) { + tileLayerZ.addTile(tilesToDraw[tileCoordKey], tileGutter); + } + tileLayerZ.finalizeAddTiles(); + } + + /** @type {Array.<number>} */ + var tileLayerZKeys = Object.keys(this.tileLayerZs_).map(Number); + tileLayerZKeys.sort(ol.array.numberSafeCompareFunction); + + var i, ii, j, origin, resolution; + var transform = goog.vec.Mat4.createNumber(); + for (i = 0, ii = tileLayerZKeys.length; i < ii; ++i) { + tileLayerZKey = tileLayerZKeys[i]; + tileLayerZ = this.tileLayerZs_[tileLayerZKey]; + if (!(tileLayerZKey in tilesToDrawByZ)) { + ol.dom.removeNode(tileLayerZ.target); + delete this.tileLayerZs_[tileLayerZKey]; + continue; + } + resolution = tileLayerZ.getResolution(); + origin = tileLayerZ.getOrigin(); + ol.vec.Mat4.makeTransform2D(transform, + frameState.size[0] / 2, frameState.size[1] / 2, + resolution / viewState.resolution, + resolution / viewState.resolution, + viewState.rotation, + (origin[0] - center[0]) / resolution, + (center[1] - origin[1]) / resolution); + tileLayerZ.setTransform(transform); + if (tileLayerZKey in newTileLayerZKeys) { + for (j = tileLayerZKey - 1; j >= 0; --j) { + if (j in this.tileLayerZs_) { + if (this.tileLayerZs_[j].target.parentNode) { + this.tileLayerZs_[j].target.parentNode.insertBefore(tileLayerZ.target, this.tileLayerZs_[j].target.nextSibling); + } + break; + } + } + if (j < 0) { + this.target.insertBefore(tileLayerZ.target, this.target.childNodes[0] || null); + } + } else { + if (!frameState.viewHints[ol.ViewHint.ANIMATING] && + !frameState.viewHints[ol.ViewHint.INTERACTING]) { + tileLayerZ.removeTilesOutsideExtent(extent, tmpTileRange); + } + } + } + + if (layerState.opacity != this.renderedOpacity_) { + this.target.style.opacity = layerState.opacity; + this.renderedOpacity_ = layerState.opacity; + } + + if (layerState.visible && !this.renderedVisible_) { + this.target.style.display = ''; + this.renderedVisible_ = true; + } + + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, + projection, extent, z, tileLayer.getPreload()); + this.scheduleExpireCache(frameState, tileSource); + this.updateLogos(frameState, tileSource); + + return true; +}; + + +/** + * @constructor + * @private + * @param {ol.tilegrid.TileGrid} tileGrid Tile grid. + * @param {ol.TileCoord} tileCoordOrigin Tile coord origin. + */ +ol.renderer.dom.TileLayerZ_ = function(tileGrid, tileCoordOrigin) { + + /** + * @type {!Element} + */ + this.target = document.createElement('DIV'); + this.target.style.position = 'absolute'; + this.target.style.width = '100%'; + this.target.style.height = '100%'; + + /** + * @private + * @type {ol.tilegrid.TileGrid} + */ + this.tileGrid_ = tileGrid; + + /** + * @private + * @type {ol.TileCoord} + */ + this.tileCoordOrigin_ = tileCoordOrigin; + + /** + * @private + * @type {ol.Coordinate} + */ + this.origin_ = + ol.extent.getTopLeft(tileGrid.getTileCoordExtent(tileCoordOrigin)); + + /** + * @private + * @type {number} + */ + this.resolution_ = tileGrid.getResolution(tileCoordOrigin[0]); + + /** + * @private + * @type {Object.<string, ol.Tile>} + */ + this.tiles_ = {}; + + /** + * @private + * @type {DocumentFragment} + */ + this.documentFragment_ = null; + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumberIdentity(); + + /** + * @private + * @type {ol.Size} + */ + this.tmpSize_ = [0, 0]; + +}; + + +/** + * @param {ol.Tile} tile Tile. + * @param {number} tileGutter Tile gutter. + */ +ol.renderer.dom.TileLayerZ_.prototype.addTile = function(tile, tileGutter) { + var tileCoord = tile.tileCoord; + var tileCoordZ = tileCoord[0]; + var tileCoordX = tileCoord[1]; + var tileCoordY = tileCoord[2]; + goog.asserts.assert(tileCoordZ == this.tileCoordOrigin_[0], + 'tileCoordZ matches z of tileCoordOrigin'); + var tileCoordKey = tileCoord.toString(); + if (tileCoordKey in this.tiles_) { + return; + } + var tileSize = ol.size.toSize( + this.tileGrid_.getTileSize(tileCoordZ), this.tmpSize_); + var image = tile.getImage(this); + var imageStyle = image.style; + // Bootstrap sets the style max-width: 100% for all images, which + // prevents the tile from being displayed in FireFox. Workaround + // by overriding the max-width style. + imageStyle.maxWidth = 'none'; + var tileElement; + var tileElementStyle; + if (tileGutter > 0) { + tileElement = document.createElement('DIV'); + tileElementStyle = tileElement.style; + tileElementStyle.overflow = 'hidden'; + tileElementStyle.width = tileSize[0] + 'px'; + tileElementStyle.height = tileSize[1] + 'px'; + imageStyle.position = 'absolute'; + imageStyle.left = -tileGutter + 'px'; + imageStyle.top = -tileGutter + 'px'; + imageStyle.width = (tileSize[0] + 2 * tileGutter) + 'px'; + imageStyle.height = (tileSize[1] + 2 * tileGutter) + 'px'; + tileElement.appendChild(image); + } else { + imageStyle.width = tileSize[0] + 'px'; + imageStyle.height = tileSize[1] + 'px'; + tileElement = image; + tileElementStyle = imageStyle; + } + tileElementStyle.position = 'absolute'; + tileElementStyle.left = + ((tileCoordX - this.tileCoordOrigin_[1]) * tileSize[0]) + 'px'; + tileElementStyle.top = + ((this.tileCoordOrigin_[2] - tileCoordY) * tileSize[1]) + 'px'; + if (!this.documentFragment_) { + this.documentFragment_ = document.createDocumentFragment(); + } + this.documentFragment_.appendChild(tileElement); + this.tiles_[tileCoordKey] = tile; +}; + + +/** + * FIXME empty description for jsdoc + */ +ol.renderer.dom.TileLayerZ_.prototype.finalizeAddTiles = function() { + if (this.documentFragment_) { + this.target.appendChild(this.documentFragment_); + this.documentFragment_ = null; + } +}; + + +/** + * @return {ol.Coordinate} Origin. + */ +ol.renderer.dom.TileLayerZ_.prototype.getOrigin = function() { + return this.origin_; +}; + + +/** + * @return {number} Resolution. + */ +ol.renderer.dom.TileLayerZ_.prototype.getResolution = function() { + return this.resolution_; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.TileRange=} opt_tileRange Temporary ol.TileRange object. + */ +ol.renderer.dom.TileLayerZ_.prototype.removeTilesOutsideExtent = function(extent, opt_tileRange) { + var tileRange = this.tileGrid_.getTileRangeForExtentAndZ( + extent, this.tileCoordOrigin_[0], opt_tileRange); + /** @type {Array.<ol.Tile>} */ + var tilesToRemove = []; + var tile, tileCoordKey; + for (tileCoordKey in this.tiles_) { + tile = this.tiles_[tileCoordKey]; + if (!tileRange.contains(tile.tileCoord)) { + tilesToRemove.push(tile); + } + } + var i, ii; + for (i = 0, ii = tilesToRemove.length; i < ii; ++i) { + tile = tilesToRemove[i]; + tileCoordKey = tile.tileCoord.toString(); + ol.dom.removeNode(tile.getImage(this)); + delete this.tiles_[tileCoordKey]; + } +}; + + +/** + * @param {goog.vec.Mat4.Number} transform Transform. + */ +ol.renderer.dom.TileLayerZ_.prototype.setTransform = function(transform) { + if (!ol.vec.Mat4.equals2D(transform, this.transform_)) { + ol.dom.transformElement2D(this.target, transform, 6); + goog.vec.Mat4.setFromArray(this.transform_, transform); + } +}; + +goog.provide('ol.renderer.dom.VectorLayer'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('goog.vec.Mat4'); +goog.require('ol.ViewHint'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.layer.Vector'); +goog.require('ol.render.Event'); +goog.require('ol.render.EventType'); +goog.require('ol.render.canvas.Immediate'); +goog.require('ol.render.canvas.ReplayGroup'); +goog.require('ol.renderer.dom.Layer'); +goog.require('ol.renderer.vector'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.dom.Layer} + * @param {ol.layer.Vector} vectorLayer Vector layer. + */ +ol.renderer.dom.VectorLayer = function(vectorLayer) { + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = ol.dom.createCanvasContext2D(); + + var target = this.context_.canvas; + // Bootstrap sets the style max-width: 100% for all images, which breaks + // prevents the image from being displayed in FireFox. Workaround by + // overriding the max-width style. + target.style.maxWidth = 'none'; + target.style.position = 'absolute'; + + ol.renderer.dom.Layer.call(this, vectorLayer, target); + + /** + * @private + * @type {boolean} + */ + this.dirty_ = false; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.renderedResolution_ = NaN; + + /** + * @private + * @type {ol.Extent} + */ + this.renderedExtent_ = ol.extent.createEmpty(); + + /** + * @private + * @type {function(ol.Feature, ol.Feature): number|null} + */ + this.renderedRenderOrder_ = null; + + /** + * @private + * @type {ol.render.canvas.ReplayGroup} + */ + this.replayGroup_ = null; + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.elementTransform_ = goog.vec.Mat4.createNumber(); + +}; +ol.inherits(ol.renderer.dom.VectorLayer, ol.renderer.dom.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.dom.VectorLayer.prototype.clearFrame = function() { + // Clear the canvas + var canvas = this.context_.canvas; + canvas.width = canvas.width; + this.renderedRevision_ = 0; +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.VectorLayer.prototype.composeFrame = function(frameState, layerState) { + + var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); + goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector, + 'layer is an instance of ol.layer.Vector'); + + var viewState = frameState.viewState; + var viewCenter = viewState.center; + var viewRotation = viewState.rotation; + var viewResolution = viewState.resolution; + var pixelRatio = frameState.pixelRatio; + var viewWidth = frameState.size[0]; + var viewHeight = frameState.size[1]; + var imageWidth = viewWidth * pixelRatio; + var imageHeight = viewHeight * pixelRatio; + + var transform = ol.vec.Mat4.makeTransform2D(this.transform_, + pixelRatio * viewWidth / 2, + pixelRatio * viewHeight / 2, + pixelRatio / viewResolution, + -pixelRatio / viewResolution, + -viewRotation, + -viewCenter[0], -viewCenter[1]); + + var context = this.context_; + + // Clear the canvas and set the correct size + context.canvas.width = imageWidth; + context.canvas.height = imageHeight; + + var elementTransform = ol.vec.Mat4.makeTransform2D(this.elementTransform_, + 0, 0, + 1 / pixelRatio, 1 / pixelRatio, + 0, + -(imageWidth - viewWidth) / 2 * pixelRatio, + -(imageHeight - viewHeight) / 2 * pixelRatio); + ol.dom.transformElement2D(context.canvas, elementTransform, 6); + + this.dispatchEvent_(ol.render.EventType.PRECOMPOSE, frameState, transform); + + var replayGroup = this.replayGroup_; + + if (replayGroup && !replayGroup.isEmpty()) { + + context.globalAlpha = layerState.opacity; + replayGroup.replay(context, pixelRatio, transform, viewRotation, + layerState.managed ? frameState.skippedFeatureUids : {}); + + this.dispatchEvent_(ol.render.EventType.RENDER, frameState, transform); + } + + this.dispatchEvent_(ol.render.EventType.POSTCOMPOSE, frameState, transform); +}; + + +/** + * @param {ol.render.EventType} type Event type. + * @param {olx.FrameState} frameState Frame state. + * @param {goog.vec.Mat4.Number} transform Transform. + * @private + */ +ol.renderer.dom.VectorLayer.prototype.dispatchEvent_ = function(type, frameState, transform) { + var context = this.context_; + var layer = this.getLayer(); + if (layer.hasListener(type)) { + var render = new ol.render.canvas.Immediate( + context, frameState.pixelRatio, frameState.extent, transform, + frameState.viewState.rotation); + var event = new ol.render.Event(type, layer, render, frameState, + context, null); + layer.dispatchEvent(event); + } +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.VectorLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg) { + if (!this.replayGroup_) { + return undefined; + } else { + var resolution = frameState.viewState.resolution; + var rotation = frameState.viewState.rotation; + var layer = this.getLayer(); + /** @type {Object.<string, boolean>} */ + var features = {}; + return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, resolution, + rotation, {}, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + goog.asserts.assert(feature !== undefined, 'received a feature'); + var key = goog.getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, layer); + } + }); + } +}; + + +/** + * Handle changes in image style state. + * @param {ol.events.Event} event Image style change event. + * @private + */ +ol.renderer.dom.VectorLayer.prototype.handleStyleImageChange_ = function(event) { + this.renderIfReadyAndVisible(); +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.VectorLayer.prototype.prepareFrame = function(frameState, layerState) { + + var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); + goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector, + 'layer is an instance of ol.layer.Vector'); + var vectorSource = vectorLayer.getSource(); + + this.updateAttributions( + frameState.attributions, vectorSource.getAttributions()); + this.updateLogos(frameState, vectorSource); + + var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; + var interacting = frameState.viewHints[ol.ViewHint.INTERACTING]; + var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); + var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); + + if (!this.dirty_ && (!updateWhileAnimating && animating) || + (!updateWhileInteracting && interacting)) { + return true; + } + + var frameStateExtent = frameState.extent; + var viewState = frameState.viewState; + var projection = viewState.projection; + var resolution = viewState.resolution; + var pixelRatio = frameState.pixelRatio; + var vectorLayerRevision = vectorLayer.getRevision(); + var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); + var vectorLayerRenderOrder = vectorLayer.getRenderOrder(); + + if (vectorLayerRenderOrder === undefined) { + vectorLayerRenderOrder = ol.renderer.vector.defaultOrder; + } + + var extent = ol.extent.buffer(frameStateExtent, + vectorLayerRenderBuffer * resolution); + + if (!this.dirty_ && + this.renderedResolution_ == resolution && + this.renderedRevision_ == vectorLayerRevision && + this.renderedRenderOrder_ == vectorLayerRenderOrder && + ol.extent.containsExtent(this.renderedExtent_, extent)) { + return true; + } + + this.replayGroup_ = null; + + this.dirty_ = false; + + var replayGroup = + new ol.render.canvas.ReplayGroup( + ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, + resolution, vectorLayer.getRenderBuffer()); + vectorSource.loadFeatures(extent, resolution, projection); + /** + * @param {ol.Feature} feature Feature. + * @this {ol.renderer.dom.VectorLayer} + */ + var renderFeature = function(feature) { + var styles; + var styleFunction = feature.getStyleFunction(); + if (styleFunction) { + styles = styleFunction.call(feature, resolution); + } else { + styleFunction = vectorLayer.getStyleFunction(); + if (styleFunction) { + styles = styleFunction(feature, resolution); + } + } + if (styles) { + var dirty = this.renderFeature( + feature, resolution, pixelRatio, styles, replayGroup); + this.dirty_ = this.dirty_ || dirty; + } + }; + if (vectorLayerRenderOrder) { + /** @type {Array.<ol.Feature>} */ + var features = []; + vectorSource.forEachFeatureInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + features.push(feature); + }, this); + features.sort(vectorLayerRenderOrder); + features.forEach(renderFeature, this); + } else { + vectorSource.forEachFeatureInExtent(extent, renderFeature, this); + } + replayGroup.finish(); + + this.renderedResolution_ = resolution; + this.renderedRevision_ = vectorLayerRevision; + this.renderedRenderOrder_ = vectorLayerRenderOrder; + this.renderedExtent_ = extent; + this.replayGroup_ = replayGroup; + + return true; +}; + + +/** + * @param {ol.Feature} feature Feature. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of + * styles. + * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. + */ +ol.renderer.dom.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { + if (!styles) { + return false; + } + var loading = false; + if (Array.isArray(styles)) { + for (var i = 0, ii = styles.length; i < ii; ++i) { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles[i], + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + } else { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles, + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + return loading; +}; + +goog.provide('ol.renderer.dom.Map'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.EventType'); +goog.require('goog.vec.Mat4'); +goog.require('ol'); +goog.require('ol.RendererType'); +goog.require('ol.array'); +goog.require('ol.css'); +goog.require('ol.dom'); +goog.require('ol.layer.Image'); +goog.require('ol.layer.Layer'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.render.Event'); +goog.require('ol.render.EventType'); +goog.require('ol.render.canvas.Immediate'); +goog.require('ol.renderer.Map'); +goog.require('ol.renderer.dom.ImageLayer'); +goog.require('ol.renderer.dom.Layer'); +goog.require('ol.renderer.dom.TileLayer'); +goog.require('ol.renderer.dom.VectorLayer'); +goog.require('ol.source.State'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.Map} + * @param {Element} container Container. + * @param {ol.Map} map Map. + */ +ol.renderer.dom.Map = function(container, map) { + + ol.renderer.Map.call(this, container, map); + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = ol.dom.createCanvasContext2D(); + var canvas = this.context_.canvas; + canvas.style.position = 'absolute'; + canvas.style.width = '100%'; + canvas.style.height = '100%'; + canvas.className = ol.css.CLASS_UNSELECTABLE; + container.insertBefore(canvas, container.childNodes[0] || null); + + /** + * @private + * @type {!goog.vec.Mat4.Number} + */ + this.transform_ = goog.vec.Mat4.createNumber(); + + /** + * @type {!Element} + * @private + */ + this.layersPane_ = document.createElement('DIV'); + this.layersPane_.className = ol.css.CLASS_UNSELECTABLE; + var style = this.layersPane_.style; + style.position = 'absolute'; + style.width = '100%'; + style.height = '100%'; + + // prevent the img context menu on mobile devices + ol.events.listen(this.layersPane_, ol.events.EventType.TOUCHSTART, + ol.events.Event.preventDefault); + + container.insertBefore(this.layersPane_, container.childNodes[0] || null); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + +}; +ol.inherits(ol.renderer.dom.Map, ol.renderer.Map); + + +/** + * @inheritDoc + */ +ol.renderer.dom.Map.prototype.disposeInternal = function() { + ol.dom.removeNode(this.layersPane_); + ol.renderer.Map.prototype.disposeInternal.call(this); +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.Map.prototype.createLayerRenderer = function(layer) { + var layerRenderer; + if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) { + layerRenderer = new ol.renderer.dom.ImageLayer(layer); + } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) { + layerRenderer = new ol.renderer.dom.TileLayer(layer); + } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) { + layerRenderer = new ol.renderer.dom.VectorLayer(layer); + } else { + goog.asserts.fail('unexpected layer configuration'); + return null; + } + return layerRenderer; +}; + + +/** + * @param {ol.render.EventType} type Event type. + * @param {olx.FrameState} frameState Frame state. + * @private + */ +ol.renderer.dom.Map.prototype.dispatchComposeEvent_ = function(type, frameState) { + var map = this.getMap(); + if (map.hasListener(type)) { + var extent = frameState.extent; + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var rotation = viewState.rotation; + var context = this.context_; + var canvas = context.canvas; + + ol.vec.Mat4.makeTransform2D(this.transform_, + canvas.width / 2, + canvas.height / 2, + pixelRatio / viewState.resolution, + -pixelRatio / viewState.resolution, + -viewState.rotation, + -viewState.center[0], -viewState.center[1]); + var vectorContext = new ol.render.canvas.Immediate(context, pixelRatio, + extent, this.transform_, rotation); + var composeEvent = new ol.render.Event(type, map, vectorContext, + frameState, context, null); + map.dispatchEvent(composeEvent); + } +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.Map.prototype.getType = function() { + return ol.RendererType.DOM; +}; + + +/** + * @inheritDoc + */ +ol.renderer.dom.Map.prototype.renderFrame = function(frameState) { + + if (!frameState) { + if (this.renderedVisible_) { + this.layersPane_.style.display = 'none'; + this.renderedVisible_ = false; + } + return; + } + + var map = this.getMap(); + if (map.hasListener(ol.render.EventType.PRECOMPOSE) || + map.hasListener(ol.render.EventType.POSTCOMPOSE)) { + var canvas = this.context_.canvas; + var pixelRatio = frameState.pixelRatio; + canvas.width = frameState.size[0] * pixelRatio; + canvas.height = frameState.size[1] * pixelRatio; + } + + this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState); + + var layerStatesArray = frameState.layerStatesArray; + ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex); + + var viewResolution = frameState.viewState.resolution; + var i, ii, layer, layerRenderer, layerState; + for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerState = layerStatesArray[i]; + layer = layerState.layer; + layerRenderer = /** @type {ol.renderer.dom.Layer} */ ( + this.getLayerRenderer(layer)); + goog.asserts.assertInstanceof(layerRenderer, ol.renderer.dom.Layer, + 'renderer is an instance of ol.renderer.dom.Layer'); + this.layersPane_.insertBefore(layerRenderer.getTarget(), this.layersPane_.childNodes[i] || null); + if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && + layerState.sourceState == ol.source.State.READY) { + if (layerRenderer.prepareFrame(frameState, layerState)) { + layerRenderer.composeFrame(frameState, layerState); + } + } else { + layerRenderer.clearFrame(); + } + } + + var layerStates = frameState.layerStates; + var layerKey; + for (layerKey in this.getLayerRenderers()) { + if (!(layerKey in layerStates)) { + layerRenderer = this.getLayerRendererByKey(layerKey); + goog.asserts.assertInstanceof(layerRenderer, ol.renderer.dom.Layer, + 'renderer is an instance of ol.renderer.dom.Layer'); + ol.dom.removeNode(layerRenderer.getTarget()); + } + } + + if (!this.renderedVisible_) { + this.layersPane_.style.display = ''; + this.renderedVisible_ = true; + } + + this.calculateMatrices2D(frameState); + this.scheduleRemoveUnusedLayerRenderers(frameState); + this.scheduleExpireIconCache(frameState); + + this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, frameState); +}; + +// Copyright 2011 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/** + * @fileoverview Constants used by the WebGL rendering, including all of the + * constants used from the WebGL context. For example, instead of using + * context.ARRAY_BUFFER, your code can use + * goog.webgl.ARRAY_BUFFER. The benefits for doing this include allowing + * the compiler to optimize your code so that the compiled code does not have to + * contain large strings to reference these properties, and reducing runtime + * property access. + * + * Values are taken from the WebGL Spec: + * https://www.khronos.org/registry/webgl/specs/1.0/#WEBGLRENDERINGCONTEXT + */ + +goog.provide('goog.webgl'); + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_BUFFER_BIT = 0x00000100; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BUFFER_BIT = 0x00000400; + + +/** + * @const + * @type {number} + */ +goog.webgl.COLOR_BUFFER_BIT = 0x00004000; + + +/** + * @const + * @type {number} + */ +goog.webgl.POINTS = 0x0000; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINES = 0x0001; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINE_LOOP = 0x0002; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINE_STRIP = 0x0003; + + +/** + * @const + * @type {number} + */ +goog.webgl.TRIANGLES = 0x0004; + + +/** + * @const + * @type {number} + */ +goog.webgl.TRIANGLE_STRIP = 0x0005; + + +/** + * @const + * @type {number} + */ +goog.webgl.TRIANGLE_FAN = 0x0006; + + +/** + * @const + * @type {number} + */ +goog.webgl.ZERO = 0; + + +/** + * @const + * @type {number} + */ +goog.webgl.ONE = 1; + + +/** + * @const + * @type {number} + */ +goog.webgl.SRC_COLOR = 0x0300; + + +/** + * @const + * @type {number} + */ +goog.webgl.ONE_MINUS_SRC_COLOR = 0x0301; + + +/** + * @const + * @type {number} + */ +goog.webgl.SRC_ALPHA = 0x0302; + + +/** + * @const + * @type {number} + */ +goog.webgl.ONE_MINUS_SRC_ALPHA = 0x0303; + + +/** + * @const + * @type {number} + */ +goog.webgl.DST_ALPHA = 0x0304; + + +/** + * @const + * @type {number} + */ +goog.webgl.ONE_MINUS_DST_ALPHA = 0x0305; + + +/** + * @const + * @type {number} + */ +goog.webgl.DST_COLOR = 0x0306; + + +/** + * @const + * @type {number} + */ +goog.webgl.ONE_MINUS_DST_COLOR = 0x0307; + + +/** + * @const + * @type {number} + */ +goog.webgl.SRC_ALPHA_SATURATE = 0x0308; + + +/** + * @const + * @type {number} + */ +goog.webgl.FUNC_ADD = 0x8006; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND_EQUATION = 0x8009; + + +/** + * Same as BLEND_EQUATION + * @const + * @type {number} + */ +goog.webgl.BLEND_EQUATION_RGB = 0x8009; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND_EQUATION_ALPHA = 0x883D; + + +/** + * @const + * @type {number} + */ +goog.webgl.FUNC_SUBTRACT = 0x800A; + + +/** + * @const + * @type {number} + */ +goog.webgl.FUNC_REVERSE_SUBTRACT = 0x800B; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND_DST_RGB = 0x80C8; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND_SRC_RGB = 0x80C9; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND_DST_ALPHA = 0x80CA; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND_SRC_ALPHA = 0x80CB; + + +/** + * @const + * @type {number} + */ +goog.webgl.CONSTANT_COLOR = 0x8001; + + +/** + * @const + * @type {number} + */ +goog.webgl.ONE_MINUS_CONSTANT_COLOR = 0x8002; + + +/** + * @const + * @type {number} + */ +goog.webgl.CONSTANT_ALPHA = 0x8003; + + +/** + * @const + * @type {number} + */ +goog.webgl.ONE_MINUS_CONSTANT_ALPHA = 0x8004; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND_COLOR = 0x8005; + + +/** + * @const + * @type {number} + */ +goog.webgl.ARRAY_BUFFER = 0x8892; + + +/** + * @const + * @type {number} + */ +goog.webgl.ELEMENT_ARRAY_BUFFER = 0x8893; + + +/** + * @const + * @type {number} + */ +goog.webgl.ARRAY_BUFFER_BINDING = 0x8894; + + +/** + * @const + * @type {number} + */ +goog.webgl.ELEMENT_ARRAY_BUFFER_BINDING = 0x8895; + + +/** + * @const + * @type {number} + */ +goog.webgl.STREAM_DRAW = 0x88E0; + + +/** + * @const + * @type {number} + */ +goog.webgl.STATIC_DRAW = 0x88E4; + + +/** + * @const + * @type {number} + */ +goog.webgl.DYNAMIC_DRAW = 0x88E8; + + +/** + * @const + * @type {number} + */ +goog.webgl.BUFFER_SIZE = 0x8764; + + +/** + * @const + * @type {number} + */ +goog.webgl.BUFFER_USAGE = 0x8765; + + +/** + * @const + * @type {number} + */ +goog.webgl.CURRENT_VERTEX_ATTRIB = 0x8626; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRONT = 0x0404; + + +/** + * @const + * @type {number} + */ +goog.webgl.BACK = 0x0405; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRONT_AND_BACK = 0x0408; + + +/** + * @const + * @type {number} + */ +goog.webgl.CULL_FACE = 0x0B44; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLEND = 0x0BE2; + + +/** + * @const + * @type {number} + */ +goog.webgl.DITHER = 0x0BD0; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_TEST = 0x0B90; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_TEST = 0x0B71; + + +/** + * @const + * @type {number} + */ +goog.webgl.SCISSOR_TEST = 0x0C11; + + +/** + * @const + * @type {number} + */ +goog.webgl.POLYGON_OFFSET_FILL = 0x8037; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLE_ALPHA_TO_COVERAGE = 0x809E; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLE_COVERAGE = 0x80A0; + + +/** + * @const + * @type {number} + */ +goog.webgl.NO_ERROR = 0; + + +/** + * @const + * @type {number} + */ +goog.webgl.INVALID_ENUM = 0x0500; + + +/** + * @const + * @type {number} + */ +goog.webgl.INVALID_VALUE = 0x0501; + + +/** + * @const + * @type {number} + */ +goog.webgl.INVALID_OPERATION = 0x0502; + + +/** + * @const + * @type {number} + */ +goog.webgl.OUT_OF_MEMORY = 0x0505; + + +/** + * @const + * @type {number} + */ +goog.webgl.CW = 0x0900; + + +/** + * @const + * @type {number} + */ +goog.webgl.CCW = 0x0901; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINE_WIDTH = 0x0B21; + + +/** + * @const + * @type {number} + */ +goog.webgl.ALIASED_POINT_SIZE_RANGE = 0x846D; + + +/** + * @const + * @type {number} + */ +goog.webgl.ALIASED_LINE_WIDTH_RANGE = 0x846E; + + +/** + * @const + * @type {number} + */ +goog.webgl.CULL_FACE_MODE = 0x0B45; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRONT_FACE = 0x0B46; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_RANGE = 0x0B70; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_WRITEMASK = 0x0B72; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_CLEAR_VALUE = 0x0B73; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_FUNC = 0x0B74; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_CLEAR_VALUE = 0x0B91; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_FUNC = 0x0B92; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_FAIL = 0x0B94; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_PASS_DEPTH_FAIL = 0x0B95; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_PASS_DEPTH_PASS = 0x0B96; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_REF = 0x0B97; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_VALUE_MASK = 0x0B93; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_WRITEMASK = 0x0B98; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BACK_FUNC = 0x8800; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BACK_FAIL = 0x8801; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BACK_PASS_DEPTH_PASS = 0x8803; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BACK_REF = 0x8CA3; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BACK_VALUE_MASK = 0x8CA4; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BACK_WRITEMASK = 0x8CA5; + + +/** + * @const + * @type {number} + */ +goog.webgl.VIEWPORT = 0x0BA2; + + +/** + * @const + * @type {number} + */ +goog.webgl.SCISSOR_BOX = 0x0C10; + + +/** + * @const + * @type {number} + */ +goog.webgl.COLOR_CLEAR_VALUE = 0x0C22; + + +/** + * @const + * @type {number} + */ +goog.webgl.COLOR_WRITEMASK = 0x0C23; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNPACK_ALIGNMENT = 0x0CF5; + + +/** + * @const + * @type {number} + */ +goog.webgl.PACK_ALIGNMENT = 0x0D05; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_TEXTURE_SIZE = 0x0D33; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_VIEWPORT_DIMS = 0x0D3A; + + +/** + * @const + * @type {number} + */ +goog.webgl.SUBPIXEL_BITS = 0x0D50; + + +/** + * @const + * @type {number} + */ +goog.webgl.RED_BITS = 0x0D52; + + +/** + * @const + * @type {number} + */ +goog.webgl.GREEN_BITS = 0x0D53; + + +/** + * @const + * @type {number} + */ +goog.webgl.BLUE_BITS = 0x0D54; + + +/** + * @const + * @type {number} + */ +goog.webgl.ALPHA_BITS = 0x0D55; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_BITS = 0x0D56; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_BITS = 0x0D57; + + +/** + * @const + * @type {number} + */ +goog.webgl.POLYGON_OFFSET_UNITS = 0x2A00; + + +/** + * @const + * @type {number} + */ +goog.webgl.POLYGON_OFFSET_FACTOR = 0x8038; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_BINDING_2D = 0x8069; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLE_BUFFERS = 0x80A8; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLES = 0x80A9; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLE_COVERAGE_VALUE = 0x80AA; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLE_COVERAGE_INVERT = 0x80AB; + + +/** + * @const + * @type {number} + */ +goog.webgl.COMPRESSED_TEXTURE_FORMATS = 0x86A3; + + +/** + * @const + * @type {number} + */ +goog.webgl.DONT_CARE = 0x1100; + + +/** + * @const + * @type {number} + */ +goog.webgl.FASTEST = 0x1101; + + +/** + * @const + * @type {number} + */ +goog.webgl.NICEST = 0x1102; + + +/** + * @const + * @type {number} + */ +goog.webgl.GENERATE_MIPMAP_HINT = 0x8192; + + +/** + * @const + * @type {number} + */ +goog.webgl.BYTE = 0x1400; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNSIGNED_BYTE = 0x1401; + + +/** + * @const + * @type {number} + */ +goog.webgl.SHORT = 0x1402; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNSIGNED_SHORT = 0x1403; + + +/** + * @const + * @type {number} + */ +goog.webgl.INT = 0x1404; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNSIGNED_INT = 0x1405; + + +/** + * @const + * @type {number} + */ +goog.webgl.FLOAT = 0x1406; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_COMPONENT = 0x1902; + + +/** + * @const + * @type {number} + */ +goog.webgl.ALPHA = 0x1906; + + +/** + * @const + * @type {number} + */ +goog.webgl.RGB = 0x1907; + + +/** + * @const + * @type {number} + */ +goog.webgl.RGBA = 0x1908; + + +/** + * @const + * @type {number} + */ +goog.webgl.LUMINANCE = 0x1909; + + +/** + * @const + * @type {number} + */ +goog.webgl.LUMINANCE_ALPHA = 0x190A; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNSIGNED_SHORT_4_4_4_4 = 0x8033; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNSIGNED_SHORT_5_5_5_1 = 0x8034; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNSIGNED_SHORT_5_6_5 = 0x8363; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAGMENT_SHADER = 0x8B30; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_SHADER = 0x8B31; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_VERTEX_ATTRIBS = 0x8869; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_VARYING_VECTORS = 0x8DFC; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_TEXTURE_IMAGE_UNITS = 0x8872; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD; + + +/** + * @const + * @type {number} + */ +goog.webgl.SHADER_TYPE = 0x8B4F; + + +/** + * @const + * @type {number} + */ +goog.webgl.DELETE_STATUS = 0x8B80; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINK_STATUS = 0x8B82; + + +/** + * @const + * @type {number} + */ +goog.webgl.VALIDATE_STATUS = 0x8B83; + + +/** + * @const + * @type {number} + */ +goog.webgl.ATTACHED_SHADERS = 0x8B85; + + +/** + * @const + * @type {number} + */ +goog.webgl.ACTIVE_UNIFORMS = 0x8B86; + + +/** + * @const + * @type {number} + */ +goog.webgl.ACTIVE_ATTRIBUTES = 0x8B89; + + +/** + * @const + * @type {number} + */ +goog.webgl.SHADING_LANGUAGE_VERSION = 0x8B8C; + + +/** + * @const + * @type {number} + */ +goog.webgl.CURRENT_PROGRAM = 0x8B8D; + + +/** + * @const + * @type {number} + */ +goog.webgl.NEVER = 0x0200; + + +/** + * @const + * @type {number} + */ +goog.webgl.LESS = 0x0201; + + +/** + * @const + * @type {number} + */ +goog.webgl.EQUAL = 0x0202; + + +/** + * @const + * @type {number} + */ +goog.webgl.LEQUAL = 0x0203; + + +/** + * @const + * @type {number} + */ +goog.webgl.GREATER = 0x0204; + + +/** + * @const + * @type {number} + */ +goog.webgl.NOTEQUAL = 0x0205; + + +/** + * @const + * @type {number} + */ +goog.webgl.GEQUAL = 0x0206; + + +/** + * @const + * @type {number} + */ +goog.webgl.ALWAYS = 0x0207; + + +/** + * @const + * @type {number} + */ +goog.webgl.KEEP = 0x1E00; + + +/** + * @const + * @type {number} + */ +goog.webgl.REPLACE = 0x1E01; + + +/** + * @const + * @type {number} + */ +goog.webgl.INCR = 0x1E02; + + +/** + * @const + * @type {number} + */ +goog.webgl.DECR = 0x1E03; + + +/** + * @const + * @type {number} + */ +goog.webgl.INVERT = 0x150A; + + +/** + * @const + * @type {number} + */ +goog.webgl.INCR_WRAP = 0x8507; + + +/** + * @const + * @type {number} + */ +goog.webgl.DECR_WRAP = 0x8508; + + +/** + * @const + * @type {number} + */ +goog.webgl.VENDOR = 0x1F00; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERER = 0x1F01; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERSION = 0x1F02; + + +/** + * @const + * @type {number} + */ +goog.webgl.NEAREST = 0x2600; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINEAR = 0x2601; + + +/** + * @const + * @type {number} + */ +goog.webgl.NEAREST_MIPMAP_NEAREST = 0x2700; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINEAR_MIPMAP_NEAREST = 0x2701; + + +/** + * @const + * @type {number} + */ +goog.webgl.NEAREST_MIPMAP_LINEAR = 0x2702; + + +/** + * @const + * @type {number} + */ +goog.webgl.LINEAR_MIPMAP_LINEAR = 0x2703; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_MAG_FILTER = 0x2800; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_MIN_FILTER = 0x2801; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_WRAP_S = 0x2802; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_WRAP_T = 0x2803; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_2D = 0x0DE1; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE = 0x1702; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_CUBE_MAP = 0x8513; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_BINDING_CUBE_MAP = 0x8514; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE0 = 0x84C0; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE1 = 0x84C1; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE2 = 0x84C2; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE3 = 0x84C3; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE4 = 0x84C4; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE5 = 0x84C5; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE6 = 0x84C6; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE7 = 0x84C7; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE8 = 0x84C8; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE9 = 0x84C9; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE10 = 0x84CA; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE11 = 0x84CB; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE12 = 0x84CC; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE13 = 0x84CD; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE14 = 0x84CE; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE15 = 0x84CF; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE16 = 0x84D0; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE17 = 0x84D1; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE18 = 0x84D2; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE19 = 0x84D3; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE20 = 0x84D4; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE21 = 0x84D5; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE22 = 0x84D6; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE23 = 0x84D7; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE24 = 0x84D8; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE25 = 0x84D9; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE26 = 0x84DA; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE27 = 0x84DB; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE28 = 0x84DC; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE29 = 0x84DD; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE30 = 0x84DE; + + +/** + * @const + * @type {number} + */ +goog.webgl.TEXTURE31 = 0x84DF; + + +/** + * @const + * @type {number} + */ +goog.webgl.ACTIVE_TEXTURE = 0x84E0; + + +/** + * @const + * @type {number} + */ +goog.webgl.REPEAT = 0x2901; + + +/** + * @const + * @type {number} + */ +goog.webgl.CLAMP_TO_EDGE = 0x812F; + + +/** + * @const + * @type {number} + */ +goog.webgl.MIRRORED_REPEAT = 0x8370; + + +/** + * @const + * @type {number} + */ +goog.webgl.FLOAT_VEC2 = 0x8B50; + + +/** + * @const + * @type {number} + */ +goog.webgl.FLOAT_VEC3 = 0x8B51; + + +/** + * @const + * @type {number} + */ +goog.webgl.FLOAT_VEC4 = 0x8B52; + + +/** + * @const + * @type {number} + */ +goog.webgl.INT_VEC2 = 0x8B53; + + +/** + * @const + * @type {number} + */ +goog.webgl.INT_VEC3 = 0x8B54; + + +/** + * @const + * @type {number} + */ +goog.webgl.INT_VEC4 = 0x8B55; + + +/** + * @const + * @type {number} + */ +goog.webgl.BOOL = 0x8B56; + + +/** + * @const + * @type {number} + */ +goog.webgl.BOOL_VEC2 = 0x8B57; + + +/** + * @const + * @type {number} + */ +goog.webgl.BOOL_VEC3 = 0x8B58; + + +/** + * @const + * @type {number} + */ +goog.webgl.BOOL_VEC4 = 0x8B59; + + +/** + * @const + * @type {number} + */ +goog.webgl.FLOAT_MAT2 = 0x8B5A; + + +/** + * @const + * @type {number} + */ +goog.webgl.FLOAT_MAT3 = 0x8B5B; + + +/** + * @const + * @type {number} + */ +goog.webgl.FLOAT_MAT4 = 0x8B5C; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLER_2D = 0x8B5E; + + +/** + * @const + * @type {number} + */ +goog.webgl.SAMPLER_CUBE = 0x8B60; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_ATTRIB_ARRAY_SIZE = 0x8623; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_ATTRIB_ARRAY_TYPE = 0x8625; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_ATTRIB_ARRAY_POINTER = 0x8645; + + +/** + * @const + * @type {number} + */ +goog.webgl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F; + + +/** + * @const + * @type {number} + */ +goog.webgl.COMPILE_STATUS = 0x8B81; + + +/** + * @const + * @type {number} + */ +goog.webgl.LOW_FLOAT = 0x8DF0; + + +/** + * @const + * @type {number} + */ +goog.webgl.MEDIUM_FLOAT = 0x8DF1; + + +/** + * @const + * @type {number} + */ +goog.webgl.HIGH_FLOAT = 0x8DF2; + + +/** + * @const + * @type {number} + */ +goog.webgl.LOW_INT = 0x8DF3; + + +/** + * @const + * @type {number} + */ +goog.webgl.MEDIUM_INT = 0x8DF4; + + +/** + * @const + * @type {number} + */ +goog.webgl.HIGH_INT = 0x8DF5; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER = 0x8D40; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER = 0x8D41; + + +/** + * @const + * @type {number} + */ +goog.webgl.RGBA4 = 0x8056; + + +/** + * @const + * @type {number} + */ +goog.webgl.RGB5_A1 = 0x8057; + + +/** + * @const + * @type {number} + */ +goog.webgl.RGB565 = 0x8D62; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_COMPONENT16 = 0x81A5; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_INDEX = 0x1901; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_INDEX8 = 0x8D48; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_STENCIL = 0x84F9; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_WIDTH = 0x8D42; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_HEIGHT = 0x8D43; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_INTERNAL_FORMAT = 0x8D44; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_RED_SIZE = 0x8D50; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_GREEN_SIZE = 0x8D51; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_BLUE_SIZE = 0x8D52; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_ALPHA_SIZE = 0x8D53; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_DEPTH_SIZE = 0x8D54; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_STENCIL_SIZE = 0x8D55; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3; + + +/** + * @const + * @type {number} + */ +goog.webgl.COLOR_ATTACHMENT0 = 0x8CE0; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_ATTACHMENT = 0x8D00; + + +/** + * @const + * @type {number} + */ +goog.webgl.STENCIL_ATTACHMENT = 0x8D20; + + +/** + * @const + * @type {number} + */ +goog.webgl.DEPTH_STENCIL_ATTACHMENT = 0x821A; + + +/** + * @const + * @type {number} + */ +goog.webgl.NONE = 0; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_COMPLETE = 0x8CD5; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_UNSUPPORTED = 0x8CDD; + + +/** + * @const + * @type {number} + */ +goog.webgl.FRAMEBUFFER_BINDING = 0x8CA6; + + +/** + * @const + * @type {number} + */ +goog.webgl.RENDERBUFFER_BINDING = 0x8CA7; + + +/** + * @const + * @type {number} + */ +goog.webgl.MAX_RENDERBUFFER_SIZE = 0x84E8; + + +/** + * @const + * @type {number} + */ +goog.webgl.INVALID_FRAMEBUFFER_OPERATION = 0x0506; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNPACK_FLIP_Y_WEBGL = 0x9240; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241; + + +/** + * @const + * @type {number} + */ +goog.webgl.CONTEXT_LOST_WEBGL = 0x9242; + + +/** + * @const + * @type {number} + */ +goog.webgl.UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243; + + +/** + * @const + * @type {number} + */ +goog.webgl.BROWSER_DEFAULT_WEBGL = 0x9244; + + +/** + * From the OES_texture_half_float extension. + * http://www.khronos.org/registry/webgl/extensions/OES_texture_half_float/ + * @const + * @type {number} + */ +goog.webgl.HALF_FLOAT_OES = 0x8D61; + + +/** + * From the OES_standard_derivatives extension. + * http://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/ + * @const + * @type {number} + */ +goog.webgl.FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B; + + +/** + * From the OES_vertex_array_object extension. + * http://www.khronos.org/registry/webgl/extensions/OES_vertex_array_object/ + * @const + * @type {number} + */ +goog.webgl.VERTEX_ARRAY_BINDING_OES = 0x85B5; + + +/** + * From the WEBGL_debug_renderer_info extension. + * http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/ + * @const + * @type {number} + */ +goog.webgl.UNMASKED_VENDOR_WEBGL = 0x9245; + + +/** + * From the WEBGL_debug_renderer_info extension. + * http://www.khronos.org/registry/webgl/extensions/WEBGL_debug_renderer_info/ + * @const + * @type {number} + */ +goog.webgl.UNMASKED_RENDERER_WEBGL = 0x9246; + + +/** + * From the WEBGL_compressed_texture_s3tc extension. + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ + * @const + * @type {number} + */ +goog.webgl.COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; + + +/** + * From the WEBGL_compressed_texture_s3tc extension. + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ + * @const + * @type {number} + */ +goog.webgl.COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; + + +/** + * From the WEBGL_compressed_texture_s3tc extension. + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ + * @const + * @type {number} + */ +goog.webgl.COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; + + +/** + * From the WEBGL_compressed_texture_s3tc extension. + * http://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_s3tc/ + * @const + * @type {number} + */ +goog.webgl.COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; + + +/** + * From the EXT_texture_filter_anisotropic extension. + * http://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/ + * @const + * @type {number} + */ +goog.webgl.TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; + + +/** + * From the EXT_texture_filter_anisotropic extension. + * http://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/ + * @const + * @type {number} + */ +goog.webgl.MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; + +goog.provide('ol.webgl.Fragment'); +goog.provide('ol.webgl.Shader'); +goog.provide('ol.webgl.Vertex'); +goog.provide('ol.webgl.shader'); + +goog.require('goog.webgl'); +goog.require('ol.functions'); +goog.require('ol.webgl'); + + +/** + * @constructor + * @param {string} source Source. + * @struct + */ +ol.webgl.Shader = function(source) { + + /** + * @private + * @type {string} + */ + this.source_ = source; + +}; + + +/** + * @return {number} Type. + */ +ol.webgl.Shader.prototype.getType = goog.abstractMethod; + + +/** + * @return {string} Source. + */ +ol.webgl.Shader.prototype.getSource = function() { + return this.source_; +}; + + +/** + * @return {boolean} Is animated? + */ +ol.webgl.Shader.prototype.isAnimated = ol.functions.FALSE; + + +/** + * @constructor + * @extends {ol.webgl.Shader} + * @param {string} source Source. + * @struct + */ +ol.webgl.shader.Fragment = function(source) { + ol.webgl.Shader.call(this, source); +}; +ol.inherits(ol.webgl.shader.Fragment, ol.webgl.Shader); + + +/** + * @inheritDoc + */ +ol.webgl.shader.Fragment.prototype.getType = function() { + return goog.webgl.FRAGMENT_SHADER; +}; + + +/** + * @constructor + * @extends {ol.webgl.Shader} + * @param {string} source Source. + * @struct + */ +ol.webgl.shader.Vertex = function(source) { + ol.webgl.Shader.call(this, source); +}; +ol.inherits(ol.webgl.shader.Vertex, ol.webgl.Shader); + + +/** + * @inheritDoc + */ +ol.webgl.shader.Vertex.prototype.getType = function() { + return goog.webgl.VERTEX_SHADER; +}; + +// This file is automatically generated, do not edit +goog.provide('ol.render.webgl.imagereplay.shader.Default'); +goog.provide('ol.render.webgl.imagereplay.shader.Default.Locations'); +goog.provide('ol.render.webgl.imagereplay.shader.DefaultFragment'); +goog.provide('ol.render.webgl.imagereplay.shader.DefaultVertex'); + +goog.require('ol.webgl.shader'); + + +/** + * @constructor + * @extends {ol.webgl.shader.Fragment} + * @struct + */ +ol.render.webgl.imagereplay.shader.DefaultFragment = function() { + ol.webgl.shader.Fragment.call(this, ol.render.webgl.imagereplay.shader.DefaultFragment.SOURCE); +}; +ol.inherits(ol.render.webgl.imagereplay.shader.DefaultFragment, ol.webgl.shader.Fragment); +goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.DefaultFragment); + + +/** + * @const + * @type {string} + */ +ol.render.webgl.imagereplay.shader.DefaultFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\nvarying float v_opacity;\n\nuniform float u_opacity;\nuniform sampler2D u_image;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_image, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n float alpha = texColor.a * v_opacity * u_opacity;\n if (alpha == 0.0) {\n discard;\n }\n gl_FragColor.a = alpha;\n}\n'; + + +/** + * @const + * @type {string} + */ +ol.render.webgl.imagereplay.shader.DefaultFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}'; + + +/** + * @const + * @type {string} + */ +ol.render.webgl.imagereplay.shader.DefaultFragment.SOURCE = goog.DEBUG ? + ol.render.webgl.imagereplay.shader.DefaultFragment.DEBUG_SOURCE : + ol.render.webgl.imagereplay.shader.DefaultFragment.OPTIMIZED_SOURCE; + + +/** + * @constructor + * @extends {ol.webgl.shader.Vertex} + * @struct + */ +ol.render.webgl.imagereplay.shader.DefaultVertex = function() { + ol.webgl.shader.Vertex.call(this, ol.render.webgl.imagereplay.shader.DefaultVertex.SOURCE); +}; +ol.inherits(ol.render.webgl.imagereplay.shader.DefaultVertex, ol.webgl.shader.Vertex); +goog.addSingletonGetter(ol.render.webgl.imagereplay.shader.DefaultVertex); + + +/** + * @const + * @type {string} + */ +ol.render.webgl.imagereplay.shader.DefaultVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\nvarying float v_opacity;\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nattribute vec2 a_offsets;\nattribute float a_opacity;\nattribute float a_rotateWithView;\n\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_offsetScaleMatrix;\nuniform mat4 u_offsetRotateMatrix;\n\nvoid main(void) {\n mat4 offsetMatrix = u_offsetScaleMatrix;\n if (a_rotateWithView == 1.0) {\n offsetMatrix = u_offsetScaleMatrix * u_offsetRotateMatrix;\n }\n vec4 offsets = offsetMatrix * vec4(a_offsets, 0., 0.);\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.) + offsets;\n v_texCoord = a_texCoord;\n v_opacity = a_opacity;\n}\n\n\n'; + + +/** + * @const + * @type {string} + */ +ol.render.webgl.imagereplay.shader.DefaultVertex.OPTIMIZED_SOURCE = 'varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.,0.);gl_Position=h*vec4(c,0.,1.)+offsets;a=d;b=f;}'; + + +/** + * @const + * @type {string} + */ +ol.render.webgl.imagereplay.shader.DefaultVertex.SOURCE = goog.DEBUG ? + ol.render.webgl.imagereplay.shader.DefaultVertex.DEBUG_SOURCE : + ol.render.webgl.imagereplay.shader.DefaultVertex.OPTIMIZED_SOURCE; + + +/** + * @constructor + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + * @struct + */ +ol.render.webgl.imagereplay.shader.Default.Locations = function(gl, program) { + + /** + * @type {WebGLUniformLocation} + */ + this.u_image = gl.getUniformLocation( + program, goog.DEBUG ? 'u_image' : 'l'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetRotateMatrix = gl.getUniformLocation( + program, goog.DEBUG ? 'u_offsetRotateMatrix' : 'j'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_offsetScaleMatrix = gl.getUniformLocation( + program, goog.DEBUG ? 'u_offsetScaleMatrix' : 'i'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, goog.DEBUG ? 'u_opacity' : 'k'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, goog.DEBUG ? 'u_projectionMatrix' : 'h'); + + /** + * @type {number} + */ + this.a_offsets = gl.getAttribLocation( + program, goog.DEBUG ? 'a_offsets' : 'e'); + + /** + * @type {number} + */ + this.a_opacity = gl.getAttribLocation( + program, goog.DEBUG ? 'a_opacity' : 'f'); + + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, goog.DEBUG ? 'a_position' : 'c'); + + /** + * @type {number} + */ + this.a_rotateWithView = gl.getAttribLocation( + program, goog.DEBUG ? 'a_rotateWithView' : 'g'); + + /** + * @type {number} + */ + this.a_texCoord = gl.getAttribLocation( + program, goog.DEBUG ? 'a_texCoord' : 'd'); +}; + +goog.provide('ol.webgl.Buffer'); + +goog.require('goog.webgl'); +goog.require('ol'); + + +/** + * @enum {number} + */ +ol.webgl.BufferUsage = { + STATIC_DRAW: goog.webgl.STATIC_DRAW, + STREAM_DRAW: goog.webgl.STREAM_DRAW, + DYNAMIC_DRAW: goog.webgl.DYNAMIC_DRAW +}; + + +/** + * @constructor + * @param {Array.<number>=} opt_arr Array. + * @param {number=} opt_usage Usage. + * @struct + */ +ol.webgl.Buffer = function(opt_arr, opt_usage) { + + /** + * @private + * @type {Array.<number>} + */ + this.arr_ = opt_arr !== undefined ? opt_arr : []; + + /** + * @private + * @type {number} + */ + this.usage_ = opt_usage !== undefined ? + opt_usage : ol.webgl.BufferUsage.STATIC_DRAW; + +}; + + +/** + * @return {Array.<number>} Array. + */ +ol.webgl.Buffer.prototype.getArray = function() { + return this.arr_; +}; + + +/** + * @return {number} Usage. + */ +ol.webgl.Buffer.prototype.getUsage = function() { + return this.usage_; +}; + +goog.provide('ol.webgl.Context'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.Disposable'); +goog.require('ol.array'); +goog.require('ol.events'); +goog.require('ol.object'); +goog.require('ol.webgl.Buffer'); +goog.require('ol.webgl.WebGLContextEventType'); + + +/** + * @classdesc + * A WebGL context for accessing low-level WebGL capabilities. + * + * @constructor + * @extends {ol.Disposable} + * @param {HTMLCanvasElement} canvas Canvas. + * @param {WebGLRenderingContext} gl GL. + */ +ol.webgl.Context = function(canvas, gl) { + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = canvas; + + /** + * @private + * @type {WebGLRenderingContext} + */ + this.gl_ = gl; + + /** + * @private + * @type {Object.<string, ol.WebglBufferCacheEntry>} + */ + this.bufferCache_ = {}; + + /** + * @private + * @type {Object.<string, WebGLShader>} + */ + this.shaderCache_ = {}; + + /** + * @private + * @type {Object.<string, WebGLProgram>} + */ + this.programCache_ = {}; + + /** + * @private + * @type {WebGLProgram} + */ + this.currentProgram_ = null; + + /** + * @private + * @type {WebGLFramebuffer} + */ + this.hitDetectionFramebuffer_ = null; + + /** + * @private + * @type {WebGLTexture} + */ + this.hitDetectionTexture_ = null; + + /** + * @private + * @type {WebGLRenderbuffer} + */ + this.hitDetectionRenderbuffer_ = null; + + /** + * @type {boolean} + */ + this.hasOESElementIndexUint = ol.array.includes( + ol.WEBGL_EXTENSIONS, 'OES_element_index_uint'); + + // use the OES_element_index_uint extension if available + if (this.hasOESElementIndexUint) { + var ext = gl.getExtension('OES_element_index_uint'); + goog.asserts.assert(ext, + 'Failed to get extension "OES_element_index_uint"'); + } + + ol.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST, + this.handleWebGLContextLost, this); + ol.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED, + this.handleWebGLContextRestored, this); + +}; +ol.inherits(ol.webgl.Context, ol.Disposable); + + +/** + * Just bind the buffer if it's in the cache. Otherwise create + * the WebGL buffer, bind it, populate it, and add an entry to + * the cache. + * @param {number} target Target. + * @param {ol.webgl.Buffer} buf Buffer. + */ +ol.webgl.Context.prototype.bindBuffer = function(target, buf) { + var gl = this.getGL(); + var arr = buf.getArray(); + var bufferKey = String(goog.getUid(buf)); + if (bufferKey in this.bufferCache_) { + var bufferCacheEntry = this.bufferCache_[bufferKey]; + gl.bindBuffer(target, bufferCacheEntry.buffer); + } else { + var buffer = gl.createBuffer(); + gl.bindBuffer(target, buffer); + goog.asserts.assert(target == goog.webgl.ARRAY_BUFFER || + target == goog.webgl.ELEMENT_ARRAY_BUFFER, + 'target is supposed to be an ARRAY_BUFFER or ELEMENT_ARRAY_BUFFER'); + var /** @type {ArrayBufferView} */ arrayBuffer; + if (target == goog.webgl.ARRAY_BUFFER) { + arrayBuffer = new Float32Array(arr); + } else if (target == goog.webgl.ELEMENT_ARRAY_BUFFER) { + arrayBuffer = this.hasOESElementIndexUint ? + new Uint32Array(arr) : new Uint16Array(arr); + } else { + goog.asserts.fail(); + } + gl.bufferData(target, arrayBuffer, buf.getUsage()); + this.bufferCache_[bufferKey] = { + buf: buf, + buffer: buffer + }; + } +}; + + +/** + * @param {ol.webgl.Buffer} buf Buffer. + */ +ol.webgl.Context.prototype.deleteBuffer = function(buf) { + var gl = this.getGL(); + var bufferKey = String(goog.getUid(buf)); + goog.asserts.assert(bufferKey in this.bufferCache_, + 'attempted to delete uncached buffer'); + var bufferCacheEntry = this.bufferCache_[bufferKey]; + if (!gl.isContextLost()) { + gl.deleteBuffer(bufferCacheEntry.buffer); + } + delete this.bufferCache_[bufferKey]; +}; + + +/** + * @inheritDoc + */ +ol.webgl.Context.prototype.disposeInternal = function() { + ol.events.unlistenAll(this.canvas_); + var gl = this.getGL(); + if (!gl.isContextLost()) { + var key; + for (key in this.bufferCache_) { + gl.deleteBuffer(this.bufferCache_[key].buffer); + } + for (key in this.programCache_) { + gl.deleteProgram(this.programCache_[key]); + } + for (key in this.shaderCache_) { + gl.deleteShader(this.shaderCache_[key]); + } + // delete objects for hit-detection + gl.deleteFramebuffer(this.hitDetectionFramebuffer_); + gl.deleteRenderbuffer(this.hitDetectionRenderbuffer_); + gl.deleteTexture(this.hitDetectionTexture_); + } +}; + + +/** + * @return {HTMLCanvasElement} Canvas. + */ +ol.webgl.Context.prototype.getCanvas = function() { + return this.canvas_; +}; + + +/** + * Get the WebGL rendering context + * @return {WebGLRenderingContext} The rendering context. + * @api + */ +ol.webgl.Context.prototype.getGL = function() { + return this.gl_; +}; + + +/** + * Get the frame buffer for hit detection. + * @return {WebGLFramebuffer} The hit detection frame buffer. + */ +ol.webgl.Context.prototype.getHitDetectionFramebuffer = function() { + if (!this.hitDetectionFramebuffer_) { + this.initHitDetectionFramebuffer_(); + } + return this.hitDetectionFramebuffer_; +}; + + +/** + * Get shader from the cache if it's in the cache. Otherwise, create + * the WebGL shader, compile it, and add entry to cache. + * @param {ol.webgl.Shader} shaderObject Shader object. + * @return {WebGLShader} Shader. + */ +ol.webgl.Context.prototype.getShader = function(shaderObject) { + var shaderKey = String(goog.getUid(shaderObject)); + if (shaderKey in this.shaderCache_) { + return this.shaderCache_[shaderKey]; + } else { + var gl = this.getGL(); + var shader = gl.createShader(shaderObject.getType()); + gl.shaderSource(shader, shaderObject.getSource()); + gl.compileShader(shader); + goog.asserts.assert( + gl.getShaderParameter(shader, goog.webgl.COMPILE_STATUS) || + gl.isContextLost(), + gl.getShaderInfoLog(shader) || 'illegal state, shader not compiled or context lost'); + this.shaderCache_[shaderKey] = shader; + return shader; + } +}; + + +/** + * Get the program from the cache if it's in the cache. Otherwise create + * the WebGL program, attach the shaders to it, and add an entry to the + * cache. + * @param {ol.webgl.shader.Fragment} fragmentShaderObject Fragment shader. + * @param {ol.webgl.shader.Vertex} vertexShaderObject Vertex shader. + * @return {WebGLProgram} Program. + */ +ol.webgl.Context.prototype.getProgram = function( + fragmentShaderObject, vertexShaderObject) { + var programKey = + goog.getUid(fragmentShaderObject) + '/' + goog.getUid(vertexShaderObject); + if (programKey in this.programCache_) { + return this.programCache_[programKey]; + } else { + var gl = this.getGL(); + var program = gl.createProgram(); + gl.attachShader(program, this.getShader(fragmentShaderObject)); + gl.attachShader(program, this.getShader(vertexShaderObject)); + gl.linkProgram(program); + goog.asserts.assert( + gl.getProgramParameter(program, goog.webgl.LINK_STATUS) || + gl.isContextLost(), + gl.getProgramInfoLog(program) || 'illegal state, shader not linked or context lost'); + this.programCache_[programKey] = program; + return program; + } +}; + + +/** + * FIXME empy description for jsdoc + */ +ol.webgl.Context.prototype.handleWebGLContextLost = function() { + ol.object.clear(this.bufferCache_); + ol.object.clear(this.shaderCache_); + ol.object.clear(this.programCache_); + this.currentProgram_ = null; + this.hitDetectionFramebuffer_ = null; + this.hitDetectionTexture_ = null; + this.hitDetectionRenderbuffer_ = null; +}; + + +/** + * FIXME empy description for jsdoc + */ +ol.webgl.Context.prototype.handleWebGLContextRestored = function() { +}; + + +/** + * Creates a 1x1 pixel framebuffer for the hit-detection. + * @private + */ +ol.webgl.Context.prototype.initHitDetectionFramebuffer_ = function() { + var gl = this.gl_; + var framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); + + var texture = ol.webgl.Context.createEmptyTexture(gl, 1, 1); + var renderbuffer = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, renderbuffer); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, 1, 1); + gl.framebufferTexture2D( + gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, + gl.RENDERBUFFER, renderbuffer); + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindRenderbuffer(gl.RENDERBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + this.hitDetectionFramebuffer_ = framebuffer; + this.hitDetectionTexture_ = texture; + this.hitDetectionRenderbuffer_ = renderbuffer; +}; + + +/** + * Use a program. If the program is already in use, this will return `false`. + * @param {WebGLProgram} program Program. + * @return {boolean} Changed. + * @api + */ +ol.webgl.Context.prototype.useProgram = function(program) { + if (program == this.currentProgram_) { + return false; + } else { + var gl = this.getGL(); + gl.useProgram(program); + this.currentProgram_ = program; + return true; + } +}; + + +/** + * @param {WebGLRenderingContext} gl WebGL rendering context. + * @param {number=} opt_wrapS wrapS. + * @param {number=} opt_wrapT wrapT. + * @return {WebGLTexture} The texture. + * @private + */ +ol.webgl.Context.createTexture_ = function(gl, opt_wrapS, opt_wrapT) { + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + + if (opt_wrapS !== undefined) { + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S, opt_wrapS); + } + if (opt_wrapT !== undefined) { + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, opt_wrapT); + } + + return texture; +}; + + +/** + * @param {WebGLRenderingContext} gl WebGL rendering context. + * @param {number} width Width. + * @param {number} height Height. + * @param {number=} opt_wrapS wrapS. + * @param {number=} opt_wrapT wrapT. + * @return {WebGLTexture} The texture. + */ +ol.webgl.Context.createEmptyTexture = function( + gl, width, height, opt_wrapS, opt_wrapT) { + var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT); + gl.texImage2D( + gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, + null); + + return texture; +}; + + +/** + * @param {WebGLRenderingContext} gl WebGL rendering context. + * @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image. + * @param {number=} opt_wrapS wrapS. + * @param {number=} opt_wrapT wrapT. + * @return {WebGLTexture} The texture. + */ +ol.webgl.Context.createTexture = function(gl, image, opt_wrapS, opt_wrapT) { + var texture = ol.webgl.Context.createTexture_(gl, opt_wrapS, opt_wrapT); + gl.texImage2D( + gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image); + + return texture; +}; + +goog.provide('ol.render.webgl.ImageReplay'); +goog.provide('ol.render.webgl.ReplayGroup'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.render.IReplayGroup'); +goog.require('ol.render.VectorContext'); +goog.require('ol.render.webgl.imagereplay.shader.Default'); +goog.require('ol.render.webgl.imagereplay.shader.Default.Locations'); +goog.require('ol.render.webgl.imagereplay.shader.DefaultFragment'); +goog.require('ol.render.webgl.imagereplay.shader.DefaultVertex'); +goog.require('ol.vec.Mat4'); +goog.require('ol.webgl.Buffer'); +goog.require('ol.webgl.Context'); + + +/** + * @constructor + * @extends {ol.render.VectorContext} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Max extent. + * @protected + * @struct + */ +ol.render.webgl.ImageReplay = function(tolerance, maxExtent) { + ol.render.VectorContext.call(this); + + /** + * @type {number|undefined} + * @private + */ + this.anchorX_ = undefined; + + /** + * @type {number|undefined} + * @private + */ + this.anchorY_ = undefined; + + /** + * The origin of the coordinate system for the point coordinates sent to + * the GPU. To eliminate jitter caused by precision problems in the GPU + * we use the "Rendering Relative to Eye" technique described in the "3D + * Engine Design for Virtual Globes" book. + * @private + * @type {ol.Coordinate} + */ + this.origin_ = ol.extent.getCenter(maxExtent); + + /** + * @type {Array.<number>} + * @private + */ + this.groupIndices_ = []; + + /** + * @type {Array.<number>} + * @private + */ + this.hitDetectionGroupIndices_ = []; + + /** + * @type {number|undefined} + * @private + */ + this.height_ = undefined; + + /** + * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} + * @private + */ + this.images_ = []; + + /** + * @type {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} + * @private + */ + this.hitDetectionImages_ = []; + + /** + * @type {number|undefined} + * @private + */ + this.imageHeight_ = undefined; + + /** + * @type {number|undefined} + * @private + */ + this.imageWidth_ = undefined; + + /** + * @type {Array.<number>} + * @private + */ + this.indices_ = []; + + /** + * @type {ol.webgl.Buffer} + * @private + */ + this.indicesBuffer_ = null; + + /** + * @private + * @type {ol.render.webgl.imagereplay.shader.Default.Locations} + */ + this.defaultLocations_ = null; + + /** + * @private + * @type {number|undefined} + */ + this.opacity_ = undefined; + + /** + * @type {!goog.vec.Mat4.Number} + * @private + */ + this.offsetRotateMatrix_ = goog.vec.Mat4.createNumberIdentity(); + + /** + * @type {!goog.vec.Mat4.Number} + * @private + */ + this.offsetScaleMatrix_ = goog.vec.Mat4.createNumberIdentity(); + + /** + * @type {number|undefined} + * @private + */ + this.originX_ = undefined; + + /** + * @type {number|undefined} + * @private + */ + this.originY_ = undefined; + + /** + * @type {!goog.vec.Mat4.Number} + * @private + */ + this.projectionMatrix_ = goog.vec.Mat4.createNumberIdentity(); + + /** + * @private + * @type {boolean|undefined} + */ + this.rotateWithView_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.scale_ = undefined; + + /** + * @type {Array.<WebGLTexture>} + * @private + */ + this.textures_ = []; + + /** + * @type {Array.<WebGLTexture>} + * @private + */ + this.hitDetectionTextures_ = []; + + /** + * @type {Array.<number>} + * @private + */ + this.vertices_ = []; + + /** + * @type {ol.webgl.Buffer} + * @private + */ + this.verticesBuffer_ = null; + + /** + * Start index per feature (the index). + * @type {Array.<number>} + * @private + */ + this.startIndices_ = []; + + /** + * Start index per feature (the feature). + * @type {Array.<ol.Feature|ol.render.Feature>} + * @private + */ + this.startIndicesFeature_ = []; + + /** + * @type {number|undefined} + * @private + */ + this.width_ = undefined; +}; +ol.inherits(ol.render.webgl.ImageReplay, ol.render.VectorContext); + + +/** + * @param {ol.webgl.Context} context WebGL context. + * @return {function()} Delete resources function. + */ +ol.render.webgl.ImageReplay.prototype.getDeleteResourcesFunction = function(context) { + // We only delete our stuff here. The shaders and the program may + // be used by other ImageReplay instances (for other layers). And + // they will be deleted when disposing of the ol.webgl.Context + // object. + goog.asserts.assert(this.verticesBuffer_, + 'verticesBuffer must not be null'); + goog.asserts.assert(this.indicesBuffer_, + 'indicesBuffer must not be null'); + var verticesBuffer = this.verticesBuffer_; + var indicesBuffer = this.indicesBuffer_; + var textures = this.textures_; + var hitDetectionTextures = this.hitDetectionTextures_; + var gl = context.getGL(); + return function() { + if (!gl.isContextLost()) { + var i, ii; + for (i = 0, ii = textures.length; i < ii; ++i) { + gl.deleteTexture(textures[i]); + } + for (i = 0, ii = hitDetectionTextures.length; i < ii; ++i) { + gl.deleteTexture(hitDetectionTextures[i]); + } + } + context.deleteBuffer(verticesBuffer); + context.deleteBuffer(indicesBuffer); + }; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {number} My end. + * @private + */ +ol.render.webgl.ImageReplay.prototype.drawCoordinates_ = function(flatCoordinates, offset, end, stride) { + goog.asserts.assert(this.anchorX_ !== undefined, 'anchorX is defined'); + goog.asserts.assert(this.anchorY_ !== undefined, 'anchorY is defined'); + goog.asserts.assert(this.height_ !== undefined, 'height is defined'); + goog.asserts.assert(this.imageHeight_ !== undefined, + 'imageHeight is defined'); + goog.asserts.assert(this.imageWidth_ !== undefined, 'imageWidth is defined'); + goog.asserts.assert(this.opacity_ !== undefined, 'opacity is defined'); + goog.asserts.assert(this.originX_ !== undefined, 'originX is defined'); + goog.asserts.assert(this.originY_ !== undefined, 'originY is defined'); + goog.asserts.assert(this.rotateWithView_ !== undefined, + 'rotateWithView is defined'); + goog.asserts.assert(this.rotation_ !== undefined, 'rotation is defined'); + goog.asserts.assert(this.scale_ !== undefined, 'scale is defined'); + goog.asserts.assert(this.width_ !== undefined, 'width is defined'); + var anchorX = this.anchorX_; + var anchorY = this.anchorY_; + var height = this.height_; + var imageHeight = this.imageHeight_; + var imageWidth = this.imageWidth_; + var opacity = this.opacity_; + var originX = this.originX_; + var originY = this.originY_; + var rotateWithView = this.rotateWithView_ ? 1.0 : 0.0; + var rotation = this.rotation_; + var scale = this.scale_; + var width = this.width_; + var cos = Math.cos(rotation); + var sin = Math.sin(rotation); + var numIndices = this.indices_.length; + var numVertices = this.vertices_.length; + var i, n, offsetX, offsetY, x, y; + for (i = offset; i < end; i += stride) { + x = flatCoordinates[i] - this.origin_[0]; + y = flatCoordinates[i + 1] - this.origin_[1]; + + // There are 4 vertices per [x, y] point, one for each corner of the + // rectangle we're going to draw. We'd use 1 vertex per [x, y] point if + // WebGL supported Geometry Shaders (which can emit new vertices), but that + // is not currently the case. + // + // And each vertex includes 8 values: the x and y coordinates, the x and + // y offsets used to calculate the position of the corner, the u and + // v texture coordinates for the corner, the opacity, and whether the + // the image should be rotated with the view (rotateWithView). + + n = numVertices / 8; + + // bottom-left corner + offsetX = -scale * anchorX; + offsetY = -scale * (height - anchorY); + this.vertices_[numVertices++] = x; + this.vertices_[numVertices++] = y; + this.vertices_[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices_[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices_[numVertices++] = originX / imageWidth; + this.vertices_[numVertices++] = (originY + height) / imageHeight; + this.vertices_[numVertices++] = opacity; + this.vertices_[numVertices++] = rotateWithView; + + // bottom-right corner + offsetX = scale * (width - anchorX); + offsetY = -scale * (height - anchorY); + this.vertices_[numVertices++] = x; + this.vertices_[numVertices++] = y; + this.vertices_[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices_[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices_[numVertices++] = (originX + width) / imageWidth; + this.vertices_[numVertices++] = (originY + height) / imageHeight; + this.vertices_[numVertices++] = opacity; + this.vertices_[numVertices++] = rotateWithView; + + // top-right corner + offsetX = scale * (width - anchorX); + offsetY = scale * anchorY; + this.vertices_[numVertices++] = x; + this.vertices_[numVertices++] = y; + this.vertices_[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices_[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices_[numVertices++] = (originX + width) / imageWidth; + this.vertices_[numVertices++] = originY / imageHeight; + this.vertices_[numVertices++] = opacity; + this.vertices_[numVertices++] = rotateWithView; + + // top-left corner + offsetX = -scale * anchorX; + offsetY = scale * anchorY; + this.vertices_[numVertices++] = x; + this.vertices_[numVertices++] = y; + this.vertices_[numVertices++] = offsetX * cos - offsetY * sin; + this.vertices_[numVertices++] = offsetX * sin + offsetY * cos; + this.vertices_[numVertices++] = originX / imageWidth; + this.vertices_[numVertices++] = originY / imageHeight; + this.vertices_[numVertices++] = opacity; + this.vertices_[numVertices++] = rotateWithView; + + this.indices_[numIndices++] = n; + this.indices_[numIndices++] = n + 1; + this.indices_[numIndices++] = n + 2; + this.indices_[numIndices++] = n; + this.indices_[numIndices++] = n + 2; + this.indices_[numIndices++] = n + 3; + } + + return numVertices; +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.ImageReplay.prototype.drawMultiPoint = function(multiPointGeometry, feature) { + this.startIndices_.push(this.indices_.length); + this.startIndicesFeature_.push(feature); + var flatCoordinates = multiPointGeometry.getFlatCoordinates(); + var stride = multiPointGeometry.getStride(); + this.drawCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.ImageReplay.prototype.drawPoint = function(pointGeometry, feature) { + this.startIndices_.push(this.indices_.length); + this.startIndicesFeature_.push(feature); + var flatCoordinates = pointGeometry.getFlatCoordinates(); + var stride = pointGeometry.getStride(); + this.drawCoordinates_( + flatCoordinates, 0, flatCoordinates.length, stride); +}; + + +/** + * @param {ol.webgl.Context} context Context. + */ +ol.render.webgl.ImageReplay.prototype.finish = function(context) { + var gl = context.getGL(); + + this.groupIndices_.push(this.indices_.length); + goog.asserts.assert(this.images_.length === this.groupIndices_.length, + 'number of images and groupIndices match'); + this.hitDetectionGroupIndices_.push(this.indices_.length); + goog.asserts.assert(this.hitDetectionImages_.length === + this.hitDetectionGroupIndices_.length, + 'number of hitDetectionImages and hitDetectionGroupIndices match'); + + // create, bind, and populate the vertices buffer + this.verticesBuffer_ = new ol.webgl.Buffer(this.vertices_); + context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.verticesBuffer_); + + var indices = this.indices_; + var bits = context.hasOESElementIndexUint ? 32 : 16; + goog.asserts.assert(indices[indices.length - 1] < Math.pow(2, bits), + 'Too large element index detected [%s] (OES_element_index_uint "%s")', + indices[indices.length - 1], context.hasOESElementIndexUint); + + // create, bind, and populate the indices buffer + this.indicesBuffer_ = new ol.webgl.Buffer(indices); + context.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); + + // create textures + /** @type {Object.<string, WebGLTexture>} */ + var texturePerImage = {}; + + this.createTextures_(this.textures_, this.images_, texturePerImage, gl); + goog.asserts.assert(this.textures_.length === this.groupIndices_.length, + 'number of textures and groupIndices match'); + + this.createTextures_(this.hitDetectionTextures_, this.hitDetectionImages_, + texturePerImage, gl); + goog.asserts.assert(this.hitDetectionTextures_.length === + this.hitDetectionGroupIndices_.length, + 'number of hitDetectionTextures and hitDetectionGroupIndices match'); + + this.anchorX_ = undefined; + this.anchorY_ = undefined; + this.height_ = undefined; + this.images_ = null; + this.hitDetectionImages_ = null; + this.imageHeight_ = undefined; + this.imageWidth_ = undefined; + this.indices_ = null; + this.opacity_ = undefined; + this.originX_ = undefined; + this.originY_ = undefined; + this.rotateWithView_ = undefined; + this.rotation_ = undefined; + this.scale_ = undefined; + this.vertices_ = null; + this.width_ = undefined; +}; + + +/** + * @private + * @param {Array.<WebGLTexture>} textures Textures. + * @param {Array.<HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} images + * Images. + * @param {Object.<string, WebGLTexture>} texturePerImage Texture cache. + * @param {WebGLRenderingContext} gl Gl. + */ +ol.render.webgl.ImageReplay.prototype.createTextures_ = function(textures, images, texturePerImage, gl) { + goog.asserts.assert(textures.length === 0, + 'upon creation, textures is empty'); + + var texture, image, uid, i; + var ii = images.length; + for (i = 0; i < ii; ++i) { + image = images[i]; + + uid = goog.getUid(image).toString(); + if (uid in texturePerImage) { + texture = texturePerImage[uid]; + } else { + texture = ol.webgl.Context.createTexture( + gl, image, goog.webgl.CLAMP_TO_EDGE, goog.webgl.CLAMP_TO_EDGE); + texturePerImage[uid] = texture; + } + textures[i] = texture; + } +}; + + +/** + * @param {ol.webgl.Context} context Context. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting + * this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.webgl.ImageReplay.prototype.replay = function(context, + center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash, + featureCallback, oneByOne, opt_hitExtent) { + var gl = context.getGL(); + + // bind the vertices buffer + goog.asserts.assert(this.verticesBuffer_, + 'verticesBuffer must not be null'); + context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.verticesBuffer_); + + // bind the indices buffer + goog.asserts.assert(this.indicesBuffer_, + 'indecesBuffer must not be null'); + context.bindBuffer(goog.webgl.ELEMENT_ARRAY_BUFFER, this.indicesBuffer_); + + // get the program + var fragmentShader = + ol.render.webgl.imagereplay.shader.DefaultFragment.getInstance(); + var vertexShader = + ol.render.webgl.imagereplay.shader.DefaultVertex.getInstance(); + var program = context.getProgram(fragmentShader, vertexShader); + + // get the locations + var locations; + if (!this.defaultLocations_) { + locations = + new ol.render.webgl.imagereplay.shader.Default.Locations(gl, program); + this.defaultLocations_ = locations; + } else { + locations = this.defaultLocations_; + } + + // use the program (FIXME: use the return value) + context.useProgram(program); + + // enable the vertex attrib arrays + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer(locations.a_position, 2, goog.webgl.FLOAT, + false, 32, 0); + + gl.enableVertexAttribArray(locations.a_offsets); + gl.vertexAttribPointer(locations.a_offsets, 2, goog.webgl.FLOAT, + false, 32, 8); + + gl.enableVertexAttribArray(locations.a_texCoord); + gl.vertexAttribPointer(locations.a_texCoord, 2, goog.webgl.FLOAT, + false, 32, 16); + + gl.enableVertexAttribArray(locations.a_opacity); + gl.vertexAttribPointer(locations.a_opacity, 1, goog.webgl.FLOAT, + false, 32, 24); + + gl.enableVertexAttribArray(locations.a_rotateWithView); + gl.vertexAttribPointer(locations.a_rotateWithView, 1, goog.webgl.FLOAT, + false, 32, 28); + + // set the "uniform" values + var projectionMatrix = this.projectionMatrix_; + ol.vec.Mat4.makeTransform2D(projectionMatrix, + 0.0, 0.0, + 2 / (resolution * size[0]), + 2 / (resolution * size[1]), + -rotation, + -(center[0] - this.origin_[0]), -(center[1] - this.origin_[1])); + + var offsetScaleMatrix = this.offsetScaleMatrix_; + goog.vec.Mat4.makeScale(offsetScaleMatrix, 2 / size[0], 2 / size[1], 1); + + var offsetRotateMatrix = this.offsetRotateMatrix_; + goog.vec.Mat4.makeIdentity(offsetRotateMatrix); + if (rotation !== 0) { + goog.vec.Mat4.rotateZ(offsetRotateMatrix, -rotation); + } + + gl.uniformMatrix4fv(locations.u_projectionMatrix, false, projectionMatrix); + gl.uniformMatrix4fv(locations.u_offsetScaleMatrix, false, offsetScaleMatrix); + gl.uniformMatrix4fv(locations.u_offsetRotateMatrix, false, + offsetRotateMatrix); + gl.uniform1f(locations.u_opacity, opacity); + + // draw! + var result; + if (featureCallback === undefined) { + this.drawReplay_(gl, context, skippedFeaturesHash, + this.textures_, this.groupIndices_); + } else { + // draw feature by feature for the hit-detection + result = this.drawHitDetectionReplay_(gl, context, skippedFeaturesHash, + featureCallback, oneByOne, opt_hitExtent); + } + + // disable the vertex attrib arrays + gl.disableVertexAttribArray(locations.a_position); + gl.disableVertexAttribArray(locations.a_offsets); + gl.disableVertexAttribArray(locations.a_texCoord); + gl.disableVertexAttribArray(locations.a_opacity); + gl.disableVertexAttribArray(locations.a_rotateWithView); + + return result; +}; + + +/** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {ol.webgl.Context} context Context. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {Array.<WebGLTexture>} textures Textures. + * @param {Array.<number>} groupIndices Texture group indices. + */ +ol.render.webgl.ImageReplay.prototype.drawReplay_ = function(gl, context, skippedFeaturesHash, textures, groupIndices) { + goog.asserts.assert(textures.length === groupIndices.length, + 'number of textures and groupIndeces match'); + var elementType = context.hasOESElementIndexUint ? + goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT; + var elementSize = context.hasOESElementIndexUint ? 4 : 2; + + if (!ol.object.isEmpty(skippedFeaturesHash)) { + this.drawReplaySkipping_( + gl, skippedFeaturesHash, textures, groupIndices, + elementType, elementSize); + } else { + var i, ii, start; + for (i = 0, ii = textures.length, start = 0; i < ii; ++i) { + gl.bindTexture(goog.webgl.TEXTURE_2D, textures[i]); + var end = groupIndices[i]; + this.drawElements_(gl, start, end, elementType, elementSize); + start = end; + } + } +}; + + +/** + * Draw the replay while paying attention to skipped features. + * + * This functions creates groups of features that can be drawn to together, + * so that the number of `drawElements` calls is minimized. + * + * For example given the following texture groups: + * + * Group 1: A B C + * Group 2: D [E] F G + * + * If feature E should be skipped, the following `drawElements` calls will be + * made: + * + * drawElements with feature A, B and C + * drawElements with feature D + * drawElements with feature F and G + * + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {Array.<WebGLTexture>} textures Textures. + * @param {Array.<number>} groupIndices Texture group indices. + * @param {number} elementType Element type. + * @param {number} elementSize Element Size. + */ +ol.render.webgl.ImageReplay.prototype.drawReplaySkipping_ = function(gl, skippedFeaturesHash, textures, groupIndices, + elementType, elementSize) { + var featureIndex = 0; + + var i, ii; + for (i = 0, ii = textures.length; i < ii; ++i) { + gl.bindTexture(goog.webgl.TEXTURE_2D, textures[i]); + var groupStart = (i > 0) ? groupIndices[i - 1] : 0; + var groupEnd = groupIndices[i]; + + var start = groupStart; + var end = groupStart; + while (featureIndex < this.startIndices_.length && + this.startIndices_[featureIndex] <= groupEnd) { + var feature = this.startIndicesFeature_[featureIndex]; + + var featureUid = goog.getUid(feature).toString(); + if (skippedFeaturesHash[featureUid] !== undefined) { + // feature should be skipped + if (start !== end) { + // draw the features so far + this.drawElements_(gl, start, end, elementType, elementSize); + } + // continue with the next feature + start = (featureIndex === this.startIndices_.length - 1) ? + groupEnd : this.startIndices_[featureIndex + 1]; + end = start; + } else { + // the feature is not skipped, augment the end index + end = (featureIndex === this.startIndices_.length - 1) ? + groupEnd : this.startIndices_[featureIndex + 1]; + } + featureIndex++; + } + + if (start !== end) { + // draw the remaining features (in case there was no skipped feature + // in this texture group, all features of a group are drawn together) + this.drawElements_(gl, start, end, elementType, elementSize); + } + } +}; + + +/** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {number} start Start index. + * @param {number} end End index. + * @param {number} elementType Element type. + * @param {number} elementSize Element Size. + */ +ol.render.webgl.ImageReplay.prototype.drawElements_ = function( + gl, start, end, elementType, elementSize) { + var numItems = end - start; + var offsetInBytes = start * elementSize; + gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes); +}; + + +/** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {ol.webgl.Context} context Context. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting + * this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplay_ = function(gl, context, skippedFeaturesHash, featureCallback, oneByOne, + opt_hitExtent) { + if (!oneByOne) { + // draw all hit-detection features in "once" (by texture group) + return this.drawHitDetectionReplayAll_(gl, context, + skippedFeaturesHash, featureCallback); + } else { + // draw hit-detection features one by one + return this.drawHitDetectionReplayOneByOne_(gl, context, + skippedFeaturesHash, featureCallback, opt_hitExtent); + } +}; + + +/** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {ol.webgl.Context} context Context. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayAll_ = function(gl, context, skippedFeaturesHash, featureCallback) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawReplay_(gl, context, skippedFeaturesHash, + this.hitDetectionTextures_, this.hitDetectionGroupIndices_); + + var result = featureCallback(null); + if (result) { + return result; + } else { + return undefined; + } +}; + + +/** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {ol.webgl.Context} context Context. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting + * this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayOneByOne_ = function(gl, context, skippedFeaturesHash, featureCallback, + opt_hitExtent) { + goog.asserts.assert(this.hitDetectionTextures_.length === + this.hitDetectionGroupIndices_.length, + 'number of hitDetectionTextures and hitDetectionGroupIndices match'); + var elementType = context.hasOESElementIndexUint ? + goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT; + var elementSize = context.hasOESElementIndexUint ? 4 : 2; + + var i, groupStart, start, end, feature, featureUid; + var featureIndex = this.startIndices_.length - 1; + for (i = this.hitDetectionTextures_.length - 1; i >= 0; --i) { + gl.bindTexture(goog.webgl.TEXTURE_2D, this.hitDetectionTextures_[i]); + groupStart = (i > 0) ? this.hitDetectionGroupIndices_[i - 1] : 0; + end = this.hitDetectionGroupIndices_[i]; + + // draw all features for this texture group + while (featureIndex >= 0 && + this.startIndices_[featureIndex] >= groupStart) { + start = this.startIndices_[featureIndex]; + feature = this.startIndicesFeature_[featureIndex]; + featureUid = goog.getUid(feature).toString(); + + if (skippedFeaturesHash[featureUid] === undefined && + feature.getGeometry() && + (opt_hitExtent === undefined || ol.extent.intersects( + /** @type {Array<number>} */ (opt_hitExtent), + feature.getGeometry().getExtent()))) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawElements_(gl, start, end, elementType, elementSize); + + var result = featureCallback(feature); + if (result) { + return result; + } + } + + end = start; + featureIndex--; + } + } + return undefined; +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.ImageReplay.prototype.setFillStrokeStyle = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.render.webgl.ImageReplay.prototype.setImageStyle = function(imageStyle) { + var anchor = imageStyle.getAnchor(); + var image = imageStyle.getImage(1); + var imageSize = imageStyle.getImageSize(); + var hitDetectionImage = imageStyle.getHitDetectionImage(1); + var hitDetectionImageSize = imageStyle.getHitDetectionImageSize(); + var opacity = imageStyle.getOpacity(); + var origin = imageStyle.getOrigin(); + var rotateWithView = imageStyle.getRotateWithView(); + var rotation = imageStyle.getRotation(); + var size = imageStyle.getSize(); + var scale = imageStyle.getScale(); + goog.asserts.assert(anchor, 'imageStyle anchor is not null'); + goog.asserts.assert(image, 'imageStyle image is not null'); + goog.asserts.assert(imageSize, + 'imageStyle imageSize is not null'); + goog.asserts.assert(hitDetectionImage, + 'imageStyle hitDetectionImage is not null'); + goog.asserts.assert(hitDetectionImageSize, + 'imageStyle hitDetectionImageSize is not null'); + goog.asserts.assert(opacity !== undefined, 'imageStyle opacity is defined'); + goog.asserts.assert(origin, 'imageStyle origin is not null'); + goog.asserts.assert(rotateWithView !== undefined, + 'imageStyle rotateWithView is defined'); + goog.asserts.assert(rotation !== undefined, 'imageStyle rotation is defined'); + goog.asserts.assert(size, 'imageStyle size is not null'); + goog.asserts.assert(scale !== undefined, 'imageStyle scale is defined'); + + var currentImage; + if (this.images_.length === 0) { + this.images_.push(image); + } else { + currentImage = this.images_[this.images_.length - 1]; + if (goog.getUid(currentImage) != goog.getUid(image)) { + this.groupIndices_.push(this.indices_.length); + goog.asserts.assert(this.groupIndices_.length === this.images_.length, + 'number of groupIndices and images match'); + this.images_.push(image); + } + } + + if (this.hitDetectionImages_.length === 0) { + this.hitDetectionImages_.push(hitDetectionImage); + } else { + currentImage = + this.hitDetectionImages_[this.hitDetectionImages_.length - 1]; + if (goog.getUid(currentImage) != goog.getUid(hitDetectionImage)) { + this.hitDetectionGroupIndices_.push(this.indices_.length); + goog.asserts.assert(this.hitDetectionGroupIndices_.length === + this.hitDetectionImages_.length, + 'number of hitDetectionGroupIndices and hitDetectionImages match'); + this.hitDetectionImages_.push(hitDetectionImage); + } + } + + this.anchorX_ = anchor[0]; + this.anchorY_ = anchor[1]; + this.height_ = size[1]; + this.imageHeight_ = imageSize[1]; + this.imageWidth_ = imageSize[0]; + this.opacity_ = opacity; + this.originX_ = origin[0]; + this.originY_ = origin[1]; + this.rotation_ = rotation; + this.rotateWithView_ = rotateWithView; + this.scale_ = scale; + this.width_ = size[0]; +}; + + +/** + * @constructor + * @implements {ol.render.IReplayGroup} + * @param {number} tolerance Tolerance. + * @param {ol.Extent} maxExtent Max extent. + * @param {number=} opt_renderBuffer Render buffer. + * @struct + */ +ol.render.webgl.ReplayGroup = function( + tolerance, maxExtent, opt_renderBuffer) { + + /** + * @type {ol.Extent} + * @private + */ + this.maxExtent_ = maxExtent; + + /** + * @type {number} + * @private + */ + this.tolerance_ = tolerance; + + /** + * @type {number|undefined} + * @private + */ + this.renderBuffer_ = opt_renderBuffer; + + /** + * ImageReplay only is supported at this point. + * @type {Object.<ol.render.ReplayType, ol.render.webgl.ImageReplay>} + * @private + */ + this.replays_ = {}; + +}; + + +/** + * @param {ol.webgl.Context} context WebGL context. + * @return {function()} Delete resources function. + */ +ol.render.webgl.ReplayGroup.prototype.getDeleteResourcesFunction = function(context) { + var functions = []; + var replayKey; + for (replayKey in this.replays_) { + functions.push( + this.replays_[replayKey].getDeleteResourcesFunction(context)); + } + return function() { + var length = functions.length; + var result; + for (var i = 0; i < length; i++) { + result = functions[i].apply(this, arguments); + } + return result; + }; +}; + + +/** + * @param {ol.webgl.Context} context Context. + */ +ol.render.webgl.ReplayGroup.prototype.finish = function(context) { + var replayKey; + for (replayKey in this.replays_) { + this.replays_[replayKey].finish(context); + } +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.ReplayGroup.prototype.getReplay = function(zIndex, replayType) { + var replay = this.replays_[replayType]; + if (replay === undefined) { + var constructor = ol.render.webgl.BATCH_CONSTRUCTORS_[replayType]; + goog.asserts.assert(constructor !== undefined, + replayType + + ' constructor missing from ol.render.webgl.BATCH_CONSTRUCTORS_'); + replay = new constructor(this.tolerance_, this.maxExtent_); + this.replays_[replayType] = replay; + } + return replay; +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.ReplayGroup.prototype.isEmpty = function() { + return ol.object.isEmpty(this.replays_); +}; + + +/** + * @param {ol.webgl.Context} context Context. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + */ +ol.render.webgl.ReplayGroup.prototype.replay = function(context, + center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash) { + var i, ii, replay; + for (i = 0, ii = ol.render.REPLAY_ORDER.length; i < ii; ++i) { + replay = this.replays_[ol.render.REPLAY_ORDER[i]]; + if (replay !== undefined) { + replay.replay(context, + center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash, + undefined, false); + } + } +}; + + +/** + * @private + * @param {ol.webgl.Context} context Context. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T|undefined} featureCallback Feature callback. + * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. + * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting + * this extent are checked. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.webgl.ReplayGroup.prototype.replayHitDetection_ = function(context, + center, resolution, rotation, size, pixelRatio, opacity, + skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent) { + var i, replay, result; + for (i = ol.render.REPLAY_ORDER.length - 1; i >= 0; --i) { + replay = this.replays_[ol.render.REPLAY_ORDER[i]]; + if (replay !== undefined) { + result = replay.replay(context, + center, resolution, rotation, size, pixelRatio, opacity, + skippedFeaturesHash, featureCallback, oneByOne, opt_hitExtent); + if (result) { + return result; + } + } + } + return undefined; +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.webgl.Context} context Context. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @param {function((ol.Feature|ol.render.Feature)): T|undefined} callback Feature callback. + * @return {T|undefined} Callback result. + * @template T + */ +ol.render.webgl.ReplayGroup.prototype.forEachFeatureAtCoordinate = function( + coordinate, context, center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash, + callback) { + var gl = context.getGL(); + gl.bindFramebuffer( + gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); + + + /** + * @type {ol.Extent} + */ + var hitExtent; + if (this.renderBuffer_ !== undefined) { + // build an extent around the coordinate, so that only features that + // intersect this extent are checked + hitExtent = ol.extent.buffer( + ol.extent.createOrUpdateFromCoordinate(coordinate), + resolution * this.renderBuffer_); + } + + return this.replayHitDetection_(context, + coordinate, resolution, rotation, ol.render.webgl.HIT_DETECTION_SIZE_, + pixelRatio, opacity, skippedFeaturesHash, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + var imageData = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); + + if (imageData[3] > 0) { + var result = callback(feature); + if (result) { + return result; + } + } + }, true, hitExtent); +}; + + +/** + * @param {ol.Coordinate} coordinate Coordinate. + * @param {ol.webgl.Context} context Context. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {number} opacity Global opacity. + * @param {Object.<string, boolean>} skippedFeaturesHash Ids of features + * to skip. + * @return {boolean} Is there a feature at the given coordinate? + */ +ol.render.webgl.ReplayGroup.prototype.hasFeatureAtCoordinate = function( + coordinate, context, center, resolution, rotation, size, pixelRatio, + opacity, skippedFeaturesHash) { + var gl = context.getGL(); + gl.bindFramebuffer( + gl.FRAMEBUFFER, context.getHitDetectionFramebuffer()); + + var hasFeature = this.replayHitDetection_(context, + coordinate, resolution, rotation, ol.render.webgl.HIT_DETECTION_SIZE_, + pixelRatio, opacity, skippedFeaturesHash, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {boolean} Is there a feature? + */ + function(feature) { + var imageData = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, imageData); + return imageData[3] > 0; + }, false); + + return hasFeature !== undefined; +}; + + +/** + * @const + * @private + * @type {Object.<ol.render.ReplayType, + * function(new: ol.render.webgl.ImageReplay, number, + * ol.Extent)>} + */ +ol.render.webgl.BATCH_CONSTRUCTORS_ = { + 'Image': ol.render.webgl.ImageReplay +}; + + +/** + * @const + * @private + * @type {Array.<number>} + */ +ol.render.webgl.HIT_DETECTION_SIZE_ = [1, 1]; + +goog.provide('ol.render.webgl.Immediate'); +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.render.VectorContext'); +goog.require('ol.render.webgl.ImageReplay'); +goog.require('ol.render.webgl.ReplayGroup'); + + +/** + * @constructor + * @extends {ol.render.VectorContext} + * @param {ol.webgl.Context} context Context. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} rotation Rotation. + * @param {ol.Size} size Size. + * @param {ol.Extent} extent Extent. + * @param {number} pixelRatio Pixel ratio. + * @struct + */ +ol.render.webgl.Immediate = function(context, center, resolution, rotation, size, extent, pixelRatio) { + ol.render.VectorContext.call(this); + + /** + * @private + */ + this.context_ = context; + + /** + * @private + */ + this.center_ = center; + + /** + * @private + */ + this.extent_ = extent; + + /** + * @private + */ + this.pixelRatio_ = pixelRatio; + + /** + * @private + */ + this.size_ = size; + + /** + * @private + */ + this.rotation_ = rotation; + + /** + * @private + */ + this.resolution_ = resolution; + + /** + * @private + * @type {ol.style.Image} + */ + this.imageStyle_ = null; + +}; +ol.inherits(ol.render.webgl.Immediate, ol.render.VectorContext); + + +/** + * Set the rendering style. Note that since this is an immediate rendering API, + * any `zIndex` on the provided style will be ignored. + * + * @param {ol.style.Style} style The rendering style. + * @api + */ +ol.render.webgl.Immediate.prototype.setStyle = function(style) { + this.setImageStyle(style.getImage()); +}; + + +/** + * Render a geometry into the canvas. Call + * {@link ol.render.webgl.Immediate#setStyle} first to set the rendering style. + * + * @param {ol.geom.Geometry|ol.render.Feature} geometry The geometry to render. + * @api + */ +ol.render.webgl.Immediate.prototype.drawGeometry = function(geometry) { + var type = geometry.getType(); + switch (type) { + case ol.geom.GeometryType.POINT: + this.drawPoint(/** @type {ol.geom.Point} */ (geometry), null); + break; + case ol.geom.GeometryType.MULTI_POINT: + this.drawMultiPoint(/** @type {ol.geom.MultiPoint} */ (geometry), null); + break; + case ol.geom.GeometryType.GEOMETRY_COLLECTION: + this.drawGeometryCollection(/** @type {ol.geom.GeometryCollection} */ (geometry), null); + break; + default: + goog.asserts.fail('Unsupported geometry type: ' + type); + } +}; + + +/** + * @inheritDoc + * @api + */ +ol.render.webgl.Immediate.prototype.drawFeature = function(feature, style) { + var geometry = style.getGeometryFunction()(feature); + if (!geometry || + !ol.extent.intersects(this.extent_, geometry.getExtent())) { + return; + } + this.setStyle(style); + goog.asserts.assert(geometry, 'geometry must be truthy'); + this.drawGeometry(geometry); +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.Immediate.prototype.drawGeometryCollection = function(geometry, data) { + var geometries = geometry.getGeometriesArray(); + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + this.drawGeometry(geometries[i]); + } +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.Immediate.prototype.drawPoint = function(geometry, data) { + var context = this.context_; + var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); + var replay = /** @type {ol.render.webgl.ImageReplay} */ ( + replayGroup.getReplay(0, ol.render.ReplayType.IMAGE)); + replay.setImageStyle(this.imageStyle_); + replay.drawPoint(geometry, data); + replay.finish(context); + // default colors + var opacity = 1; + var skippedFeatures = {}; + var featureCallback; + var oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.Immediate.prototype.drawMultiPoint = function(geometry, data) { + var context = this.context_; + var replayGroup = new ol.render.webgl.ReplayGroup(1, this.extent_); + var replay = /** @type {ol.render.webgl.ImageReplay} */ ( + replayGroup.getReplay(0, ol.render.ReplayType.IMAGE)); + replay.setImageStyle(this.imageStyle_); + replay.drawMultiPoint(geometry, data); + replay.finish(context); + var opacity = 1; + var skippedFeatures = {}; + var featureCallback; + var oneByOne = false; + replay.replay(this.context_, this.center_, this.resolution_, this.rotation_, + this.size_, this.pixelRatio_, opacity, skippedFeatures, featureCallback, + oneByOne); + replay.getDeleteResourcesFunction(context)(); +}; + + +/** + * @inheritDoc + */ +ol.render.webgl.Immediate.prototype.setImageStyle = function(imageStyle) { + this.imageStyle_ = imageStyle; +}; + +// This file is automatically generated, do not edit +goog.provide('ol.renderer.webgl.map.shader.Default'); +goog.provide('ol.renderer.webgl.map.shader.Default.Locations'); +goog.provide('ol.renderer.webgl.map.shader.DefaultFragment'); +goog.provide('ol.renderer.webgl.map.shader.DefaultVertex'); + +goog.require('ol.webgl.shader'); + + +/** + * @constructor + * @extends {ol.webgl.shader.Fragment} + * @struct + */ +ol.renderer.webgl.map.shader.DefaultFragment = function() { + ol.webgl.shader.Fragment.call(this, ol.renderer.webgl.map.shader.DefaultFragment.SOURCE); +}; +ol.inherits(ol.renderer.webgl.map.shader.DefaultFragment, ol.webgl.shader.Fragment); +goog.addSingletonGetter(ol.renderer.webgl.map.shader.DefaultFragment); + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.map.shader.DefaultFragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform float u_opacity;\nuniform sampler2D u_texture;\n\nvoid main(void) {\n vec4 texColor = texture2D(u_texture, v_texCoord);\n gl_FragColor.rgb = texColor.rgb;\n gl_FragColor.a = texColor.a * u_opacity;\n}\n'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.map.shader.DefaultFragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform float f;uniform sampler2D g;void main(void){vec4 texColor=texture2D(g,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*f;}'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.map.shader.DefaultFragment.SOURCE = goog.DEBUG ? + ol.renderer.webgl.map.shader.DefaultFragment.DEBUG_SOURCE : + ol.renderer.webgl.map.shader.DefaultFragment.OPTIMIZED_SOURCE; + + +/** + * @constructor + * @extends {ol.webgl.shader.Vertex} + * @struct + */ +ol.renderer.webgl.map.shader.DefaultVertex = function() { + ol.webgl.shader.Vertex.call(this, ol.renderer.webgl.map.shader.DefaultVertex.SOURCE); +}; +ol.inherits(ol.renderer.webgl.map.shader.DefaultVertex, ol.webgl.shader.Vertex); +goog.addSingletonGetter(ol.renderer.webgl.map.shader.DefaultVertex); + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.map.shader.DefaultVertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\n\nuniform mat4 u_texCoordMatrix;\nuniform mat4 u_projectionMatrix;\n\nvoid main(void) {\n gl_Position = u_projectionMatrix * vec4(a_position, 0., 1.);\n v_texCoord = (u_texCoordMatrix * vec4(a_texCoord, 0., 1.)).st;\n}\n\n\n'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.map.shader.DefaultVertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.map.shader.DefaultVertex.SOURCE = goog.DEBUG ? + ol.renderer.webgl.map.shader.DefaultVertex.DEBUG_SOURCE : + ol.renderer.webgl.map.shader.DefaultVertex.OPTIMIZED_SOURCE; + + +/** + * @constructor + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + * @struct + */ +ol.renderer.webgl.map.shader.Default.Locations = function(gl, program) { + + /** + * @type {WebGLUniformLocation} + */ + this.u_opacity = gl.getUniformLocation( + program, goog.DEBUG ? 'u_opacity' : 'f'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_projectionMatrix = gl.getUniformLocation( + program, goog.DEBUG ? 'u_projectionMatrix' : 'e'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_texCoordMatrix = gl.getUniformLocation( + program, goog.DEBUG ? 'u_texCoordMatrix' : 'd'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_texture = gl.getUniformLocation( + program, goog.DEBUG ? 'u_texture' : 'g'); + + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, goog.DEBUG ? 'a_position' : 'b'); + + /** + * @type {number} + */ + this.a_texCoord = gl.getAttribLocation( + program, goog.DEBUG ? 'a_texCoord' : 'c'); +}; + +goog.provide('ol.renderer.webgl.Layer'); + +goog.require('goog.vec.Mat4'); +goog.require('goog.webgl'); +goog.require('ol.layer.Layer'); +goog.require('ol.render.Event'); +goog.require('ol.render.EventType'); +goog.require('ol.render.webgl.Immediate'); +goog.require('ol.renderer.Layer'); +goog.require('ol.renderer.webgl.map.shader.Default'); +goog.require('ol.renderer.webgl.map.shader.Default.Locations'); +goog.require('ol.renderer.webgl.map.shader.DefaultFragment'); +goog.require('ol.renderer.webgl.map.shader.DefaultVertex'); +goog.require('ol.webgl.Buffer'); +goog.require('ol.webgl.Context'); + + +/** + * @constructor + * @extends {ol.renderer.Layer} + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. + * @param {ol.layer.Layer} layer Layer. + */ +ol.renderer.webgl.Layer = function(mapRenderer, layer) { + + ol.renderer.Layer.call(this, layer); + + /** + * @protected + * @type {ol.renderer.webgl.Map} + */ + this.mapRenderer = mapRenderer; + + /** + * @private + * @type {ol.webgl.Buffer} + */ + this.arrayBuffer_ = new ol.webgl.Buffer([ + -1, -1, 0, 0, + 1, -1, 1, 0, + -1, 1, 0, 1, + 1, 1, 1, 1 + ]); + + /** + * @protected + * @type {WebGLTexture} + */ + this.texture = null; + + /** + * @protected + * @type {WebGLFramebuffer} + */ + this.framebuffer = null; + + /** + * @protected + * @type {number|undefined} + */ + this.framebufferDimension = undefined; + + /** + * @protected + * @type {!goog.vec.Mat4.Number} + */ + this.texCoordMatrix = goog.vec.Mat4.createNumber(); + + /** + * @protected + * @type {!goog.vec.Mat4.Number} + */ + this.projectionMatrix = goog.vec.Mat4.createNumberIdentity(); + + /** + * @private + * @type {ol.renderer.webgl.map.shader.Default.Locations} + */ + this.defaultLocations_ = null; + +}; +ol.inherits(ol.renderer.webgl.Layer, ol.renderer.Layer); + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {number} framebufferDimension Framebuffer dimension. + * @protected + */ +ol.renderer.webgl.Layer.prototype.bindFramebuffer = function(frameState, framebufferDimension) { + + var gl = this.mapRenderer.getGL(); + + if (this.framebufferDimension === undefined || + this.framebufferDimension != framebufferDimension) { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLFramebuffer} framebuffer Framebuffer. + * @param {WebGLTexture} texture Texture. + */ + var postRenderFunction = function(gl, framebuffer, texture) { + if (!gl.isContextLost()) { + gl.deleteFramebuffer(framebuffer); + gl.deleteTexture(texture); + } + }.bind(null, gl, this.framebuffer, this.texture); + + frameState.postRenderFunctions.push( + /** @type {ol.PostRenderFunction} */ (postRenderFunction) + ); + + var texture = ol.webgl.Context.createEmptyTexture( + gl, framebufferDimension, framebufferDimension); + + var framebuffer = gl.createFramebuffer(); + gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, framebuffer); + gl.framebufferTexture2D(goog.webgl.FRAMEBUFFER, + goog.webgl.COLOR_ATTACHMENT0, goog.webgl.TEXTURE_2D, texture, 0); + + this.texture = texture; + this.framebuffer = framebuffer; + this.framebufferDimension = framebufferDimension; + + } else { + gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, this.framebuffer); + } + +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @param {ol.webgl.Context} context Context. + */ +ol.renderer.webgl.Layer.prototype.composeFrame = function(frameState, layerState, context) { + + this.dispatchComposeEvent_( + ol.render.EventType.PRECOMPOSE, context, frameState); + + context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.arrayBuffer_); + + var gl = context.getGL(); + + var fragmentShader = + ol.renderer.webgl.map.shader.DefaultFragment.getInstance(); + var vertexShader = ol.renderer.webgl.map.shader.DefaultVertex.getInstance(); + + var program = context.getProgram(fragmentShader, vertexShader); + + var locations; + if (!this.defaultLocations_) { + locations = + new ol.renderer.webgl.map.shader.Default.Locations(gl, program); + this.defaultLocations_ = locations; + } else { + locations = this.defaultLocations_; + } + + if (context.useProgram(program)) { + gl.enableVertexAttribArray(locations.a_position); + gl.vertexAttribPointer( + locations.a_position, 2, goog.webgl.FLOAT, false, 16, 0); + gl.enableVertexAttribArray(locations.a_texCoord); + gl.vertexAttribPointer( + locations.a_texCoord, 2, goog.webgl.FLOAT, false, 16, 8); + gl.uniform1i(locations.u_texture, 0); + } + + gl.uniformMatrix4fv( + locations.u_texCoordMatrix, false, this.getTexCoordMatrix()); + gl.uniformMatrix4fv(locations.u_projectionMatrix, false, + this.getProjectionMatrix()); + gl.uniform1f(locations.u_opacity, layerState.opacity); + gl.bindTexture(goog.webgl.TEXTURE_2D, this.getTexture()); + gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4); + + this.dispatchComposeEvent_( + ol.render.EventType.POSTCOMPOSE, context, frameState); + +}; + + +/** + * @param {ol.render.EventType} type Event type. + * @param {ol.webgl.Context} context WebGL context. + * @param {olx.FrameState} frameState Frame state. + * @private + */ +ol.renderer.webgl.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState) { + var layer = this.getLayer(); + if (layer.hasListener(type)) { + var viewState = frameState.viewState; + var resolution = viewState.resolution; + var pixelRatio = frameState.pixelRatio; + var extent = frameState.extent; + var center = viewState.center; + var rotation = viewState.rotation; + var size = frameState.size; + + var render = new ol.render.webgl.Immediate( + context, center, resolution, rotation, size, extent, pixelRatio); + var composeEvent = new ol.render.Event( + type, layer, render, frameState, null, context); + layer.dispatchEvent(composeEvent); + } +}; + + +/** + * @return {!goog.vec.Mat4.Number} Matrix. + */ +ol.renderer.webgl.Layer.prototype.getTexCoordMatrix = function() { + return this.texCoordMatrix; +}; + + +/** + * @return {WebGLTexture} Texture. + */ +ol.renderer.webgl.Layer.prototype.getTexture = function() { + return this.texture; +}; + + +/** + * @return {!goog.vec.Mat4.Number} Matrix. + */ +ol.renderer.webgl.Layer.prototype.getProjectionMatrix = function() { + return this.projectionMatrix; +}; + + +/** + * Handle webglcontextlost. + */ +ol.renderer.webgl.Layer.prototype.handleWebGLContextLost = function() { + this.texture = null; + this.framebuffer = null; + this.framebufferDimension = undefined; +}; + + +/** + * @param {olx.FrameState} frameState Frame state. + * @param {ol.LayerState} layerState Layer state. + * @param {ol.webgl.Context} context Context. + * @return {boolean} whether composeFrame should be called. + */ +ol.renderer.webgl.Layer.prototype.prepareFrame = goog.abstractMethod; + +goog.provide('ol.renderer.webgl.ImageLayer'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('goog.webgl'); +goog.require('ol.ImageBase'); +goog.require('ol.ViewHint'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.functions'); +goog.require('ol.layer.Image'); +goog.require('ol.proj'); +goog.require('ol.renderer.webgl.Layer'); +goog.require('ol.source.ImageVector'); +goog.require('ol.vec.Mat4'); +goog.require('ol.webgl.Context'); + + +/** + * @constructor + * @extends {ol.renderer.webgl.Layer} + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. + * @param {ol.layer.Image} imageLayer Tile layer. + */ +ol.renderer.webgl.ImageLayer = function(mapRenderer, imageLayer) { + + ol.renderer.webgl.Layer.call(this, mapRenderer, imageLayer); + + /** + * The last rendered image. + * @private + * @type {?ol.ImageBase} + */ + this.image_ = null; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.hitCanvasContext_ = null; + + /** + * @private + * @type {?goog.vec.Mat4.Number} + */ + this.hitTransformationMatrix_ = null; + +}; +ol.inherits(ol.renderer.webgl.ImageLayer, ol.renderer.webgl.Layer); + + +/** + * @param {ol.ImageBase} image Image. + * @private + * @return {WebGLTexture} Texture. + */ +ol.renderer.webgl.ImageLayer.prototype.createTexture_ = function(image) { + + // We meet the conditions to work with non-power of two textures. + // http://www.khronos.org/webgl/wiki/WebGL_and_OpenGL_Differences#Non-Power_of_Two_Texture_Support + // http://learningwebgl.com/blog/?p=2101 + + var imageElement = image.getImage(); + var gl = this.mapRenderer.getGL(); + + return ol.webgl.Context.createTexture( + gl, imageElement, goog.webgl.CLAMP_TO_EDGE, goog.webgl.CLAMP_TO_EDGE); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg) { + var layer = this.getLayer(); + var source = layer.getSource(); + var resolution = frameState.viewState.resolution; + var rotation = frameState.viewState.rotation; + var skippedFeatureUids = frameState.skippedFeatureUids; + return source.forEachFeatureAtCoordinate( + coordinate, resolution, rotation, skippedFeatureUids, + + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + return callback.call(thisArg, feature, layer); + }); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.prepareFrame = function(frameState, layerState, context) { + + var gl = this.mapRenderer.getGL(); + + var pixelRatio = frameState.pixelRatio; + var viewState = frameState.viewState; + var viewCenter = viewState.center; + var viewResolution = viewState.resolution; + var viewRotation = viewState.rotation; + + var image = this.image_; + var texture = this.texture; + var imageLayer = this.getLayer(); + goog.asserts.assertInstanceof(imageLayer, ol.layer.Image, + 'layer is an instance of ol.layer.Image'); + var imageSource = imageLayer.getSource(); + + var hints = frameState.viewHints; + + var renderedExtent = frameState.extent; + if (layerState.extent !== undefined) { + renderedExtent = ol.extent.getIntersection( + renderedExtent, layerState.extent); + } + if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && + !ol.extent.isEmpty(renderedExtent)) { + var projection = viewState.projection; + if (!ol.ENABLE_RASTER_REPROJECTION) { + var sourceProjection = imageSource.getProjection(); + if (sourceProjection) { + goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection), + 'projection and sourceProjection are equivalent'); + projection = sourceProjection; + } + } + var image_ = imageSource.getImage(renderedExtent, viewResolution, + pixelRatio, projection); + if (image_) { + var loaded = this.loadImage(image_); + if (loaded) { + image = image_; + texture = this.createTexture_(image_); + if (this.texture) { + /** + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLTexture} texture Texture. + */ + var postRenderFunction = function(gl, texture) { + if (!gl.isContextLost()) { + gl.deleteTexture(texture); + } + }.bind(null, gl, this.texture); + frameState.postRenderFunctions.push( + /** @type {ol.PostRenderFunction} */ (postRenderFunction) + ); + } + } + } + } + + if (image) { + goog.asserts.assert(texture, 'texture is truthy'); + + var canvas = this.mapRenderer.getContext().getCanvas(); + + this.updateProjectionMatrix_(canvas.width, canvas.height, + pixelRatio, viewCenter, viewResolution, viewRotation, + image.getExtent()); + this.hitTransformationMatrix_ = null; + + // Translate and scale to flip the Y coord. + var texCoordMatrix = this.texCoordMatrix; + goog.vec.Mat4.makeIdentity(texCoordMatrix); + goog.vec.Mat4.scale(texCoordMatrix, 1, -1, 1); + goog.vec.Mat4.translate(texCoordMatrix, 0, -1, 0); + + this.image_ = image; + this.texture = texture; + + this.updateAttributions(frameState.attributions, image.getAttributions()); + this.updateLogos(frameState, imageSource); + } + + return true; +}; + + +/** + * @param {number} canvasWidth Canvas width. + * @param {number} canvasHeight Canvas height. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.Coordinate} viewCenter View center. + * @param {number} viewResolution View resolution. + * @param {number} viewRotation View rotation. + * @param {ol.Extent} imageExtent Image extent. + * @private + */ +ol.renderer.webgl.ImageLayer.prototype.updateProjectionMatrix_ = function(canvasWidth, canvasHeight, pixelRatio, + viewCenter, viewResolution, viewRotation, imageExtent) { + + var canvasExtentWidth = canvasWidth * viewResolution; + var canvasExtentHeight = canvasHeight * viewResolution; + + var projectionMatrix = this.projectionMatrix; + goog.vec.Mat4.makeIdentity(projectionMatrix); + goog.vec.Mat4.scale(projectionMatrix, + pixelRatio * 2 / canvasExtentWidth, + pixelRatio * 2 / canvasExtentHeight, 1); + goog.vec.Mat4.rotateZ(projectionMatrix, -viewRotation); + goog.vec.Mat4.translate(projectionMatrix, + imageExtent[0] - viewCenter[0], + imageExtent[1] - viewCenter[1], + 0); + goog.vec.Mat4.scale(projectionMatrix, + (imageExtent[2] - imageExtent[0]) / 2, + (imageExtent[3] - imageExtent[1]) / 2, + 1); + goog.vec.Mat4.translate(projectionMatrix, 1, 1, 0); + +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) { + var hasFeature = this.forEachFeatureAtCoordinate( + coordinate, frameState, ol.functions.TRUE, this); + return hasFeature !== undefined; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.ImageLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { + if (!this.image_ || !this.image_.getImage()) { + return undefined; + } + + if (this.getLayer().getSource() instanceof ol.source.ImageVector) { + // for ImageVector sources use the original hit-detection logic, + // so that for example also transparent polygons are detected + var coordinate = pixel.slice(); + ol.vec.Mat4.multVec2( + frameState.pixelToCoordinateMatrix, coordinate, coordinate); + var hasFeature = this.forEachFeatureAtCoordinate( + coordinate, frameState, ol.functions.TRUE, this); + + if (hasFeature) { + return callback.call(thisArg, this.getLayer()); + } else { + return undefined; + } + } else { + var imageSize = + [this.image_.getImage().width, this.image_.getImage().height]; + + if (!this.hitTransformationMatrix_) { + this.hitTransformationMatrix_ = this.getHitTransformationMatrix_( + frameState.size, imageSize); + } + + var pixelOnFrameBuffer = [0, 0]; + ol.vec.Mat4.multVec2( + this.hitTransformationMatrix_, pixel, pixelOnFrameBuffer); + + if (pixelOnFrameBuffer[0] < 0 || pixelOnFrameBuffer[0] > imageSize[0] || + pixelOnFrameBuffer[1] < 0 || pixelOnFrameBuffer[1] > imageSize[1]) { + // outside the image, no need to check + return undefined; + } + + if (!this.hitCanvasContext_) { + this.hitCanvasContext_ = ol.dom.createCanvasContext2D(1, 1); + } + + this.hitCanvasContext_.clearRect(0, 0, 1, 1); + this.hitCanvasContext_.drawImage(this.image_.getImage(), + pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, 0, 0, 1, 1); + + var imageData = this.hitCanvasContext_.getImageData(0, 0, 1, 1).data; + if (imageData[3] > 0) { + return callback.call(thisArg, this.getLayer()); + } else { + return undefined; + } + } +}; + + +/** + * The transformation matrix to get the pixel on the image for a + * pixel on the map. + * @param {ol.Size} mapSize The map size. + * @param {ol.Size} imageSize The image size. + * @return {goog.vec.Mat4.Number} The transformation matrix. + * @private + */ +ol.renderer.webgl.ImageLayer.prototype.getHitTransformationMatrix_ = function(mapSize, imageSize) { + // the first matrix takes a map pixel, flips the y-axis and scales to + // a range between -1 ... 1 + var mapCoordMatrix = goog.vec.Mat4.createNumber(); + goog.vec.Mat4.makeIdentity(mapCoordMatrix); + goog.vec.Mat4.translate(mapCoordMatrix, -1, -1, 0); + goog.vec.Mat4.scale(mapCoordMatrix, 2 / mapSize[0], 2 / mapSize[1], 1); + goog.vec.Mat4.translate(mapCoordMatrix, 0, mapSize[1], 0); + goog.vec.Mat4.scale(mapCoordMatrix, 1, -1, 1); + + // the second matrix is the inverse of the projection matrix used in the + // shader for drawing + var projectionMatrixInv = goog.vec.Mat4.createNumber(); + goog.vec.Mat4.invert(this.projectionMatrix, projectionMatrixInv); + + // the third matrix scales to the image dimensions and flips the y-axis again + var imageCoordMatrix = goog.vec.Mat4.createNumber(); + goog.vec.Mat4.makeIdentity(imageCoordMatrix); + goog.vec.Mat4.translate(imageCoordMatrix, 0, imageSize[1], 0); + goog.vec.Mat4.scale(imageCoordMatrix, 1, -1, 1); + goog.vec.Mat4.scale(imageCoordMatrix, imageSize[0] / 2, imageSize[1] / 2, 1); + goog.vec.Mat4.translate(imageCoordMatrix, 1, 1, 0); + + var transformMatrix = goog.vec.Mat4.createNumber(); + goog.vec.Mat4.multMat( + imageCoordMatrix, projectionMatrixInv, transformMatrix); + goog.vec.Mat4.multMat( + transformMatrix, mapCoordMatrix, transformMatrix); + + return transformMatrix; +}; + +// This file is automatically generated, do not edit +goog.provide('ol.renderer.webgl.tilelayer.shader'); +goog.provide('ol.renderer.webgl.tilelayer.shader.Locations'); +goog.provide('ol.renderer.webgl.tilelayer.shader.Fragment'); +goog.provide('ol.renderer.webgl.tilelayer.shader.Vertex'); + +goog.require('ol.webgl.shader'); + + +/** + * @constructor + * @extends {ol.webgl.shader.Fragment} + * @struct + */ +ol.renderer.webgl.tilelayer.shader.Fragment = function() { + ol.webgl.shader.Fragment.call(this, ol.renderer.webgl.tilelayer.shader.Fragment.SOURCE); +}; +ol.inherits(ol.renderer.webgl.tilelayer.shader.Fragment, ol.webgl.shader.Fragment); +goog.addSingletonGetter(ol.renderer.webgl.tilelayer.shader.Fragment); + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.tilelayer.shader.Fragment.DEBUG_SOURCE = 'precision mediump float;\nvarying vec2 v_texCoord;\n\n\nuniform sampler2D u_texture;\n\nvoid main(void) {\n gl_FragColor = texture2D(u_texture, v_texCoord);\n}\n'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.tilelayer.shader.Fragment.OPTIMIZED_SOURCE = 'precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.tilelayer.shader.Fragment.SOURCE = goog.DEBUG ? + ol.renderer.webgl.tilelayer.shader.Fragment.DEBUG_SOURCE : + ol.renderer.webgl.tilelayer.shader.Fragment.OPTIMIZED_SOURCE; + + +/** + * @constructor + * @extends {ol.webgl.shader.Vertex} + * @struct + */ +ol.renderer.webgl.tilelayer.shader.Vertex = function() { + ol.webgl.shader.Vertex.call(this, ol.renderer.webgl.tilelayer.shader.Vertex.SOURCE); +}; +ol.inherits(ol.renderer.webgl.tilelayer.shader.Vertex, ol.webgl.shader.Vertex); +goog.addSingletonGetter(ol.renderer.webgl.tilelayer.shader.Vertex); + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.tilelayer.shader.Vertex.DEBUG_SOURCE = 'varying vec2 v_texCoord;\n\n\nattribute vec2 a_position;\nattribute vec2 a_texCoord;\nuniform vec4 u_tileOffset;\n\nvoid main(void) {\n gl_Position = vec4(a_position * u_tileOffset.xy + u_tileOffset.zw, 0., 1.);\n v_texCoord = a_texCoord;\n}\n\n\n'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.tilelayer.shader.Vertex.OPTIMIZED_SOURCE = 'varying vec2 a;attribute vec2 b;attribute vec2 c;uniform vec4 d;void main(void){gl_Position=vec4(b*d.xy+d.zw,0.,1.);a=c;}'; + + +/** + * @const + * @type {string} + */ +ol.renderer.webgl.tilelayer.shader.Vertex.SOURCE = goog.DEBUG ? + ol.renderer.webgl.tilelayer.shader.Vertex.DEBUG_SOURCE : + ol.renderer.webgl.tilelayer.shader.Vertex.OPTIMIZED_SOURCE; + + +/** + * @constructor + * @param {WebGLRenderingContext} gl GL. + * @param {WebGLProgram} program Program. + * @struct + */ +ol.renderer.webgl.tilelayer.shader.Locations = function(gl, program) { + + /** + * @type {WebGLUniformLocation} + */ + this.u_texture = gl.getUniformLocation( + program, goog.DEBUG ? 'u_texture' : 'e'); + + /** + * @type {WebGLUniformLocation} + */ + this.u_tileOffset = gl.getUniformLocation( + program, goog.DEBUG ? 'u_tileOffset' : 'd'); + + /** + * @type {number} + */ + this.a_position = gl.getAttribLocation( + program, goog.DEBUG ? 'a_position' : 'b'); + + /** + * @type {number} + */ + this.a_texCoord = gl.getAttribLocation( + program, goog.DEBUG ? 'a_texCoord' : 'c'); +}; + +// FIXME large resolutions lead to too large framebuffers :-( +// FIXME animated shaders! check in redraw + +goog.provide('ol.renderer.webgl.TileLayer'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('goog.vec.Vec4'); +goog.require('goog.webgl'); +goog.require('ol.TileRange'); +goog.require('ol.TileState'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.layer.Tile'); +goog.require('ol.math'); +goog.require('ol.renderer.webgl.Layer'); +goog.require('ol.renderer.webgl.tilelayer.shader.Fragment'); +goog.require('ol.renderer.webgl.tilelayer.shader.Locations'); +goog.require('ol.renderer.webgl.tilelayer.shader.Vertex'); +goog.require('ol.size'); +goog.require('ol.vec.Mat4'); +goog.require('ol.webgl.Buffer'); + + +/** + * @constructor + * @extends {ol.renderer.webgl.Layer} + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. + * @param {ol.layer.Tile} tileLayer Tile layer. + */ +ol.renderer.webgl.TileLayer = function(mapRenderer, tileLayer) { + + ol.renderer.webgl.Layer.call(this, mapRenderer, tileLayer); + + /** + * @private + * @type {ol.webgl.shader.Fragment} + */ + this.fragmentShader_ = + ol.renderer.webgl.tilelayer.shader.Fragment.getInstance(); + + /** + * @private + * @type {ol.webgl.shader.Vertex} + */ + this.vertexShader_ = ol.renderer.webgl.tilelayer.shader.Vertex.getInstance(); + + /** + * @private + * @type {ol.renderer.webgl.tilelayer.shader.Locations} + */ + this.locations_ = null; + + /** + * @private + * @type {ol.webgl.Buffer} + */ + this.renderArrayBuffer_ = new ol.webgl.Buffer([ + 0, 0, 0, 1, + 1, 0, 1, 1, + 0, 1, 0, 0, + 1, 1, 1, 0 + ]); + + /** + * @private + * @type {ol.TileRange} + */ + this.renderedTileRange_ = null; + + /** + * @private + * @type {ol.Extent} + */ + this.renderedFramebufferExtent_ = null; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = -1; + + /** + * @private + * @type {ol.Size} + */ + this.tmpSize_ = [0, 0]; + +}; +ol.inherits(ol.renderer.webgl.TileLayer, ol.renderer.webgl.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() { + var context = this.mapRenderer.getContext(); + context.deleteBuffer(this.renderArrayBuffer_); + ol.renderer.webgl.Layer.prototype.disposeInternal.call(this); +}; + + +/** + * Create a function that adds loaded tiles to the tile lookup. + * @param {ol.source.Tile} source Tile source. + * @param {ol.proj.Projection} projection Projection of the tiles. + * @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded + * tiles by zoom level. + * @return {function(number, ol.TileRange):boolean} A function that can be + * called with a zoom level and a tile range to add loaded tiles to the + * lookup. + * @protected + */ +ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder = function(source, projection, tiles) { + var mapRenderer = this.mapRenderer; + + return ( + /** + * @param {number} zoom Zoom level. + * @param {ol.TileRange} tileRange Tile range. + * @return {boolean} The tile range is fully loaded. + */ + function(zoom, tileRange) { + function callback(tile) { + var loaded = mapRenderer.isTileTextureLoaded(tile); + if (loaded) { + if (!tiles[zoom]) { + tiles[zoom] = {}; + } + tiles[zoom][tile.tileCoord.toString()] = tile; + } + return loaded; + } + return source.forEachLoadedTile(projection, zoom, tileRange, callback); + }); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.TileLayer.prototype.handleWebGLContextLost = function() { + ol.renderer.webgl.Layer.prototype.handleWebGLContextLost.call(this); + this.locations_ = null; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.TileLayer.prototype.prepareFrame = function(frameState, layerState, context) { + + var mapRenderer = this.mapRenderer; + var gl = context.getGL(); + + var viewState = frameState.viewState; + var projection = viewState.projection; + + var tileLayer = this.getLayer(); + goog.asserts.assertInstanceof(tileLayer, ol.layer.Tile, + 'layer is an instance of ol.layer.Tile'); + var tileSource = tileLayer.getSource(); + var tileGrid = tileSource.getTileGridForProjection(projection); + var z = tileGrid.getZForResolution(viewState.resolution); + var tileResolution = tileGrid.getResolution(z); + + var tilePixelSize = + tileSource.getTilePixelSize(z, frameState.pixelRatio, projection); + var pixelRatio = tilePixelSize[0] / + ol.size.toSize(tileGrid.getTileSize(z), this.tmpSize_)[0]; + var tilePixelResolution = tileResolution / pixelRatio; + var tileGutter = tileSource.getGutter(projection); + + var center = viewState.center; + var extent; + if (tileResolution == viewState.resolution) { + center = this.snapCenterToPixel(center, tileResolution, frameState.size); + extent = ol.extent.getForViewAndSize( + center, tileResolution, viewState.rotation, frameState.size); + } else { + extent = frameState.extent; + } + var tileRange = tileGrid.getTileRangeForExtentAndResolution( + extent, tileResolution); + + var framebufferExtent; + if (this.renderedTileRange_ && + this.renderedTileRange_.equals(tileRange) && + this.renderedRevision_ == tileSource.getRevision()) { + framebufferExtent = this.renderedFramebufferExtent_; + } else { + + var tileRangeSize = tileRange.getSize(); + + var maxDimension = Math.max( + tileRangeSize[0] * tilePixelSize[0], + tileRangeSize[1] * tilePixelSize[1]); + var framebufferDimension = ol.math.roundUpToPowerOfTwo(maxDimension); + var framebufferExtentDimension = tilePixelResolution * framebufferDimension; + var origin = tileGrid.getOrigin(z); + var minX = origin[0] + + tileRange.minX * tilePixelSize[0] * tilePixelResolution; + var minY = origin[1] + + tileRange.minY * tilePixelSize[1] * tilePixelResolution; + framebufferExtent = [ + minX, minY, + minX + framebufferExtentDimension, minY + framebufferExtentDimension + ]; + + this.bindFramebuffer(frameState, framebufferDimension); + gl.viewport(0, 0, framebufferDimension, framebufferDimension); + + gl.clearColor(0, 0, 0, 0); + gl.clear(goog.webgl.COLOR_BUFFER_BIT); + gl.disable(goog.webgl.BLEND); + + var program = context.getProgram(this.fragmentShader_, this.vertexShader_); + context.useProgram(program); + if (!this.locations_) { + this.locations_ = + new ol.renderer.webgl.tilelayer.shader.Locations(gl, program); + } + + context.bindBuffer(goog.webgl.ARRAY_BUFFER, this.renderArrayBuffer_); + gl.enableVertexAttribArray(this.locations_.a_position); + gl.vertexAttribPointer( + this.locations_.a_position, 2, goog.webgl.FLOAT, false, 16, 0); + gl.enableVertexAttribArray(this.locations_.a_texCoord); + gl.vertexAttribPointer( + this.locations_.a_texCoord, 2, goog.webgl.FLOAT, false, 16, 8); + gl.uniform1i(this.locations_.u_texture, 0); + + /** + * @type {Object.<number, Object.<string, ol.Tile>>} + */ + var tilesToDrawByZ = {}; + tilesToDrawByZ[z] = {}; + + var findLoadedTiles = this.createLoadedTileFinder( + tileSource, projection, tilesToDrawByZ); + + var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError(); + var allTilesLoaded = true; + var tmpExtent = ol.extent.createEmpty(); + var tmpTileRange = new ol.TileRange(0, 0, 0, 0); + var childTileRange, drawable, fullyLoaded, tile, tileState; + var x, y, tileExtent; + for (x = tileRange.minX; x <= tileRange.maxX; ++x) { + for (y = tileRange.minY; y <= tileRange.maxY; ++y) { + + tile = tileSource.getTile(z, x, y, pixelRatio, projection); + if (layerState.extent !== undefined) { + // ignore tiles outside layer extent + tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); + if (!ol.extent.intersects(tileExtent, layerState.extent)) { + continue; + } + } + tileState = tile.getState(); + drawable = tileState == ol.TileState.LOADED || + tileState == ol.TileState.EMPTY || + tileState == ol.TileState.ERROR && !useInterimTilesOnError; + if (!drawable && tile.interimTile) { + tile = tile.interimTile; + } + goog.asserts.assert(tile); + tileState = tile.getState(); + if (tileState == ol.TileState.LOADED) { + if (mapRenderer.isTileTextureLoaded(tile)) { + tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; + continue; + } + } else if (tileState == ol.TileState.EMPTY || + (tileState == ol.TileState.ERROR && + !useInterimTilesOnError)) { + continue; + } + + allTilesLoaded = false; + fullyLoaded = tileGrid.forEachTileCoordParentTileRange( + tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); + if (!fullyLoaded) { + childTileRange = tileGrid.getTileCoordChildTileRange( + tile.tileCoord, tmpTileRange, tmpExtent); + if (childTileRange) { + findLoadedTiles(z + 1, childTileRange); + } + } + + } + + } + + /** @type {Array.<number>} */ + var zs = Object.keys(tilesToDrawByZ).map(Number); + zs.sort(ol.array.numberSafeCompareFunction); + var u_tileOffset = goog.vec.Vec4.createFloat32(); + var i, ii, sx, sy, tileKey, tilesToDraw, tx, ty; + for (i = 0, ii = zs.length; i < ii; ++i) { + tilesToDraw = tilesToDrawByZ[zs[i]]; + for (tileKey in tilesToDraw) { + tile = tilesToDraw[tileKey]; + tileExtent = tileGrid.getTileCoordExtent(tile.tileCoord, tmpExtent); + sx = 2 * (tileExtent[2] - tileExtent[0]) / + framebufferExtentDimension; + sy = 2 * (tileExtent[3] - tileExtent[1]) / + framebufferExtentDimension; + tx = 2 * (tileExtent[0] - framebufferExtent[0]) / + framebufferExtentDimension - 1; + ty = 2 * (tileExtent[1] - framebufferExtent[1]) / + framebufferExtentDimension - 1; + goog.vec.Vec4.setFromValues(u_tileOffset, sx, sy, tx, ty); + gl.uniform4fv(this.locations_.u_tileOffset, u_tileOffset); + mapRenderer.bindTileTexture(tile, tilePixelSize, + tileGutter * pixelRatio, goog.webgl.LINEAR, goog.webgl.LINEAR); + gl.drawArrays(goog.webgl.TRIANGLE_STRIP, 0, 4); + } + } + + if (allTilesLoaded) { + this.renderedTileRange_ = tileRange; + this.renderedFramebufferExtent_ = framebufferExtent; + this.renderedRevision_ = tileSource.getRevision(); + } else { + this.renderedTileRange_ = null; + this.renderedFramebufferExtent_ = null; + this.renderedRevision_ = -1; + frameState.animate = true; + } + + } + + this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); + var tileTextureQueue = mapRenderer.getTileTextureQueue(); + this.manageTilePyramid( + frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, + tileLayer.getPreload(), + /** + * @param {ol.Tile} tile Tile. + */ + function(tile) { + if (tile.getState() == ol.TileState.LOADED && + !mapRenderer.isTileTextureLoaded(tile) && + !tileTextureQueue.isKeyQueued(tile.getKey())) { + tileTextureQueue.enqueue([ + tile, + tileGrid.getTileCoordCenter(tile.tileCoord), + tileGrid.getResolution(tile.tileCoord[0]), + tilePixelSize, tileGutter * pixelRatio + ]); + } + }, this); + this.scheduleExpireCache(frameState, tileSource); + this.updateLogos(frameState, tileSource); + + var texCoordMatrix = this.texCoordMatrix; + goog.vec.Mat4.makeIdentity(texCoordMatrix); + goog.vec.Mat4.translate(texCoordMatrix, + (center[0] - framebufferExtent[0]) / + (framebufferExtent[2] - framebufferExtent[0]), + (center[1] - framebufferExtent[1]) / + (framebufferExtent[3] - framebufferExtent[1]), + 0); + if (viewState.rotation !== 0) { + goog.vec.Mat4.rotateZ(texCoordMatrix, viewState.rotation); + } + goog.vec.Mat4.scale(texCoordMatrix, + frameState.size[0] * viewState.resolution / + (framebufferExtent[2] - framebufferExtent[0]), + frameState.size[1] * viewState.resolution / + (framebufferExtent[3] - framebufferExtent[1]), + 1); + goog.vec.Mat4.translate(texCoordMatrix, + -0.5, + -0.5, + 0); + + return true; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.TileLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { + if (!this.framebuffer) { + return undefined; + } + + var pixelOnMapScaled = [ + pixel[0] / frameState.size[0], + (frameState.size[1] - pixel[1]) / frameState.size[1]]; + + var pixelOnFrameBufferScaled = [0, 0]; + ol.vec.Mat4.multVec2( + this.texCoordMatrix, pixelOnMapScaled, pixelOnFrameBufferScaled); + var pixelOnFrameBuffer = [ + pixelOnFrameBufferScaled[0] * this.framebufferDimension, + pixelOnFrameBufferScaled[1] * this.framebufferDimension]; + + var gl = this.mapRenderer.getContext().getGL(); + gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer); + var imageData = new Uint8Array(4); + gl.readPixels(pixelOnFrameBuffer[0], pixelOnFrameBuffer[1], 1, 1, + gl.RGBA, gl.UNSIGNED_BYTE, imageData); + + if (imageData[3] > 0) { + return callback.call(thisArg, this.getLayer()); + } else { + return undefined; + } +}; + +goog.provide('ol.renderer.webgl.VectorLayer'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.ViewHint'); +goog.require('ol.extent'); +goog.require('ol.layer.Vector'); +goog.require('ol.render.webgl.ReplayGroup'); +goog.require('ol.renderer.vector'); +goog.require('ol.renderer.webgl.Layer'); +goog.require('ol.vec.Mat4'); + + +/** + * @constructor + * @extends {ol.renderer.webgl.Layer} + * @param {ol.renderer.webgl.Map} mapRenderer Map renderer. + * @param {ol.layer.Vector} vectorLayer Vector layer. + */ +ol.renderer.webgl.VectorLayer = function(mapRenderer, vectorLayer) { + + ol.renderer.webgl.Layer.call(this, mapRenderer, vectorLayer); + + /** + * @private + * @type {boolean} + */ + this.dirty_ = false; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.renderedResolution_ = NaN; + + /** + * @private + * @type {ol.Extent} + */ + this.renderedExtent_ = ol.extent.createEmpty(); + + /** + * @private + * @type {function(ol.Feature, ol.Feature): number|null} + */ + this.renderedRenderOrder_ = null; + + /** + * @private + * @type {ol.render.webgl.ReplayGroup} + */ + this.replayGroup_ = null; + + /** + * The last layer state. + * @private + * @type {?ol.LayerState} + */ + this.layerState_ = null; + +}; +ol.inherits(ol.renderer.webgl.VectorLayer, ol.renderer.webgl.Layer); + + +/** + * @inheritDoc + */ +ol.renderer.webgl.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) { + this.layerState_ = layerState; + var viewState = frameState.viewState; + var replayGroup = this.replayGroup_; + if (replayGroup && !replayGroup.isEmpty()) { + replayGroup.replay(context, + viewState.center, viewState.resolution, viewState.rotation, + frameState.size, frameState.pixelRatio, layerState.opacity, + layerState.managed ? frameState.skippedFeatureUids : {}); + } + +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.VectorLayer.prototype.disposeInternal = function() { + var replayGroup = this.replayGroup_; + if (replayGroup) { + var context = this.mapRenderer.getContext(); + replayGroup.getDeleteResourcesFunction(context)(); + this.replayGroup_ = null; + } + ol.renderer.webgl.Layer.prototype.disposeInternal.call(this); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.VectorLayer.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg) { + if (!this.replayGroup_ || !this.layerState_) { + return undefined; + } else { + var context = this.mapRenderer.getContext(); + var viewState = frameState.viewState; + var layer = this.getLayer(); + var layerState = this.layerState_; + /** @type {Object.<string, boolean>} */ + var features = {}; + return this.replayGroup_.forEachFeatureAtCoordinate(coordinate, + context, viewState.center, viewState.resolution, viewState.rotation, + frameState.size, frameState.pixelRatio, layerState.opacity, + {}, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @return {?} Callback result. + */ + function(feature) { + goog.asserts.assert(feature !== undefined, 'received a feature'); + var key = goog.getUid(feature).toString(); + if (!(key in features)) { + features[key] = true; + return callback.call(thisArg, feature, layer); + } + }); + } +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.VectorLayer.prototype.hasFeatureAtCoordinate = function(coordinate, frameState) { + if (!this.replayGroup_ || !this.layerState_) { + return false; + } else { + var context = this.mapRenderer.getContext(); + var viewState = frameState.viewState; + var layerState = this.layerState_; + return this.replayGroup_.hasFeatureAtCoordinate(coordinate, + context, viewState.center, viewState.resolution, viewState.rotation, + frameState.size, frameState.pixelRatio, layerState.opacity, + frameState.skippedFeatureUids); + } +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.VectorLayer.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg) { + var coordinate = pixel.slice(); + ol.vec.Mat4.multVec2( + frameState.pixelToCoordinateMatrix, coordinate, coordinate); + var hasFeature = this.hasFeatureAtCoordinate(coordinate, frameState); + + if (hasFeature) { + return callback.call(thisArg, this.getLayer()); + } else { + return undefined; + } +}; + + +/** + * Handle changes in image style state. + * @param {ol.events.Event} event Image style change event. + * @private + */ +ol.renderer.webgl.VectorLayer.prototype.handleStyleImageChange_ = function(event) { + this.renderIfReadyAndVisible(); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.VectorLayer.prototype.prepareFrame = function(frameState, layerState, context) { + + var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); + goog.asserts.assertInstanceof(vectorLayer, ol.layer.Vector, + 'layer is an instance of ol.layer.Vector'); + var vectorSource = vectorLayer.getSource(); + + this.updateAttributions( + frameState.attributions, vectorSource.getAttributions()); + this.updateLogos(frameState, vectorSource); + + var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; + var interacting = frameState.viewHints[ol.ViewHint.INTERACTING]; + var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); + var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); + + if (!this.dirty_ && (!updateWhileAnimating && animating) || + (!updateWhileInteracting && interacting)) { + return true; + } + + var frameStateExtent = frameState.extent; + var viewState = frameState.viewState; + var projection = viewState.projection; + var resolution = viewState.resolution; + var pixelRatio = frameState.pixelRatio; + var vectorLayerRevision = vectorLayer.getRevision(); + var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); + var vectorLayerRenderOrder = vectorLayer.getRenderOrder(); + + if (vectorLayerRenderOrder === undefined) { + vectorLayerRenderOrder = ol.renderer.vector.defaultOrder; + } + + var extent = ol.extent.buffer(frameStateExtent, + vectorLayerRenderBuffer * resolution); + + if (!this.dirty_ && + this.renderedResolution_ == resolution && + this.renderedRevision_ == vectorLayerRevision && + this.renderedRenderOrder_ == vectorLayerRenderOrder && + ol.extent.containsExtent(this.renderedExtent_, extent)) { + return true; + } + + if (this.replayGroup_) { + frameState.postRenderFunctions.push( + this.replayGroup_.getDeleteResourcesFunction(context)); + } + + this.dirty_ = false; + + var replayGroup = new ol.render.webgl.ReplayGroup( + ol.renderer.vector.getTolerance(resolution, pixelRatio), + extent, vectorLayer.getRenderBuffer()); + vectorSource.loadFeatures(extent, resolution, projection); + /** + * @param {ol.Feature} feature Feature. + * @this {ol.renderer.webgl.VectorLayer} + */ + var renderFeature = function(feature) { + var styles; + var styleFunction = feature.getStyleFunction(); + if (styleFunction) { + styles = styleFunction.call(feature, resolution); + } else { + styleFunction = vectorLayer.getStyleFunction(); + if (styleFunction) { + styles = styleFunction(feature, resolution); + } + } + if (styles) { + var dirty = this.renderFeature( + feature, resolution, pixelRatio, styles, replayGroup); + this.dirty_ = this.dirty_ || dirty; + } + }; + if (vectorLayerRenderOrder) { + /** @type {Array.<ol.Feature>} */ + var features = []; + vectorSource.forEachFeatureInExtent(extent, + /** + * @param {ol.Feature} feature Feature. + */ + function(feature) { + features.push(feature); + }, this); + features.sort(vectorLayerRenderOrder); + features.forEach(renderFeature, this); + } else { + vectorSource.forEachFeatureInExtent(extent, renderFeature, this); + } + replayGroup.finish(context); + + this.renderedResolution_ = resolution; + this.renderedRevision_ = vectorLayerRevision; + this.renderedRenderOrder_ = vectorLayerRenderOrder; + this.renderedExtent_ = extent; + this.replayGroup_ = replayGroup; + + return true; +}; + + +/** + * @param {ol.Feature} feature Feature. + * @param {number} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of + * styles. + * @param {ol.render.webgl.ReplayGroup} replayGroup Replay group. + * @return {boolean} `true` if an image is loading. + */ +ol.renderer.webgl.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { + if (!styles) { + return false; + } + var loading = false; + if (Array.isArray(styles)) { + for (var i = 0, ii = styles.length; i < ii; ++i) { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles[i], + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + } else { + loading = ol.renderer.vector.renderFeature( + replayGroup, feature, styles, + ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), + this.handleStyleImageChange_, this) || loading; + } + return loading; +}; + +// FIXME check against gl.getParameter(webgl.MAX_TEXTURE_SIZE) + +goog.provide('ol.renderer.webgl.Map'); + +goog.require('goog.asserts'); +goog.require('goog.webgl'); +goog.require('ol'); +goog.require('ol.RendererType'); +goog.require('ol.array'); +goog.require('ol.css'); +goog.require('ol.dom'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.layer.Image'); +goog.require('ol.layer.Layer'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.render.Event'); +goog.require('ol.render.EventType'); +goog.require('ol.render.webgl.Immediate'); +goog.require('ol.renderer.Map'); +goog.require('ol.renderer.webgl.ImageLayer'); +goog.require('ol.renderer.webgl.Layer'); +goog.require('ol.renderer.webgl.TileLayer'); +goog.require('ol.renderer.webgl.VectorLayer'); +goog.require('ol.source.State'); +goog.require('ol.structs.LRUCache'); +goog.require('ol.structs.PriorityQueue'); +goog.require('ol.webgl'); +goog.require('ol.webgl.Context'); +goog.require('ol.webgl.WebGLContextEventType'); + + +/** + * @constructor + * @extends {ol.renderer.Map} + * @param {Element} container Container. + * @param {ol.Map} map Map. + */ +ol.renderer.webgl.Map = function(container, map) { + + ol.renderer.Map.call(this, container, map); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = /** @type {HTMLCanvasElement} */ + (document.createElement('CANVAS')); + this.canvas_.style.width = '100%'; + this.canvas_.style.height = '100%'; + this.canvas_.className = ol.css.CLASS_UNSELECTABLE; + container.insertBefore(this.canvas_, container.childNodes[0] || null); + + /** + * @private + * @type {number} + */ + this.clipTileCanvasWidth_ = 0; + + /** + * @private + * @type {number} + */ + this.clipTileCanvasHeight_ = 0; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.clipTileContext_ = ol.dom.createCanvasContext2D(); + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = true; + + /** + * @private + * @type {WebGLRenderingContext} + */ + this.gl_ = ol.webgl.getContext(this.canvas_, { + antialias: true, + depth: false, + failIfMajorPerformanceCaveat: true, + preserveDrawingBuffer: false, + stencil: true + }); + goog.asserts.assert(this.gl_, 'got a WebGLRenderingContext'); + + /** + * @private + * @type {ol.webgl.Context} + */ + this.context_ = new ol.webgl.Context(this.canvas_, this.gl_); + + ol.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.LOST, + this.handleWebGLContextLost, this); + ol.events.listen(this.canvas_, ol.webgl.WebGLContextEventType.RESTORED, + this.handleWebGLContextRestored, this); + + /** + * @private + * @type {ol.structs.LRUCache.<ol.WebglTextureCacheEntry|null>} + */ + this.textureCache_ = new ol.structs.LRUCache(); + + /** + * @private + * @type {ol.Coordinate} + */ + this.focus_ = null; + + /** + * @private + * @type {ol.structs.PriorityQueue.<Array>} + */ + this.tileTextureQueue_ = new ol.structs.PriorityQueue( + /** + * @param {Array.<*>} element Element. + * @return {number} Priority. + * @this {ol.renderer.webgl.Map} + */ + (function(element) { + var tileCenter = /** @type {ol.Coordinate} */ (element[1]); + var tileResolution = /** @type {number} */ (element[2]); + var deltaX = tileCenter[0] - this.focus_[0]; + var deltaY = tileCenter[1] - this.focus_[1]; + return 65536 * Math.log(tileResolution) + + Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; + }).bind(this), + /** + * @param {Array.<*>} element Element. + * @return {string} Key. + */ + function(element) { + return /** @type {ol.Tile} */ (element[0]).getKey(); + }); + + + /** + * @param {ol.Map} map Map. + * @param {?olx.FrameState} frameState Frame state. + * @return {boolean} false. + * @this {ol.renderer.webgl.Map} + */ + this.loadNextTileTexture_ = + function(map, frameState) { + if (!this.tileTextureQueue_.isEmpty()) { + this.tileTextureQueue_.reprioritize(); + var element = this.tileTextureQueue_.dequeue(); + var tile = /** @type {ol.Tile} */ (element[0]); + var tileSize = /** @type {ol.Size} */ (element[3]); + var tileGutter = /** @type {number} */ (element[4]); + this.bindTileTexture( + tile, tileSize, tileGutter, goog.webgl.LINEAR, goog.webgl.LINEAR); + } + return false; + }.bind(this); + + + /** + * @private + * @type {number} + */ + this.textureCacheFrameMarkerCount_ = 0; + + this.initializeGL_(); + +}; +ol.inherits(ol.renderer.webgl.Map, ol.renderer.Map); + + +/** + * @param {ol.Tile} tile Tile. + * @param {ol.Size} tileSize Tile size. + * @param {number} tileGutter Tile gutter. + * @param {number} magFilter Mag filter. + * @param {number} minFilter Min filter. + */ +ol.renderer.webgl.Map.prototype.bindTileTexture = function(tile, tileSize, tileGutter, magFilter, minFilter) { + var gl = this.getGL(); + var tileKey = tile.getKey(); + if (this.textureCache_.containsKey(tileKey)) { + var textureCacheEntry = this.textureCache_.get(tileKey); + goog.asserts.assert(textureCacheEntry, + 'a texture cache entry exists for key %s', tileKey); + gl.bindTexture(goog.webgl.TEXTURE_2D, textureCacheEntry.texture); + if (textureCacheEntry.magFilter != magFilter) { + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter); + textureCacheEntry.magFilter = magFilter; + } + if (textureCacheEntry.minFilter != minFilter) { + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, minFilter); + textureCacheEntry.minFilter = minFilter; + } + } else { + var texture = gl.createTexture(); + gl.bindTexture(goog.webgl.TEXTURE_2D, texture); + if (tileGutter > 0) { + var clipTileCanvas = this.clipTileContext_.canvas; + var clipTileContext = this.clipTileContext_; + if (this.clipTileCanvasWidth_ !== tileSize[0] || + this.clipTileCanvasHeight_ !== tileSize[1]) { + clipTileCanvas.width = tileSize[0]; + clipTileCanvas.height = tileSize[1]; + this.clipTileCanvasWidth_ = tileSize[0]; + this.clipTileCanvasHeight_ = tileSize[1]; + } else { + clipTileContext.clearRect(0, 0, tileSize[0], tileSize[1]); + } + clipTileContext.drawImage(tile.getImage(), tileGutter, tileGutter, + tileSize[0], tileSize[1], 0, 0, tileSize[0], tileSize[1]); + gl.texImage2D(goog.webgl.TEXTURE_2D, 0, + goog.webgl.RGBA, goog.webgl.RGBA, + goog.webgl.UNSIGNED_BYTE, clipTileCanvas); + } else { + gl.texImage2D(goog.webgl.TEXTURE_2D, 0, + goog.webgl.RGBA, goog.webgl.RGBA, + goog.webgl.UNSIGNED_BYTE, tile.getImage()); + } + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MAG_FILTER, magFilter); + gl.texParameteri( + goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_MIN_FILTER, minFilter); + gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_S, + goog.webgl.CLAMP_TO_EDGE); + gl.texParameteri(goog.webgl.TEXTURE_2D, goog.webgl.TEXTURE_WRAP_T, + goog.webgl.CLAMP_TO_EDGE); + this.textureCache_.set(tileKey, { + texture: texture, + magFilter: magFilter, + minFilter: minFilter + }); + } +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.createLayerRenderer = function(layer) { + if (ol.ENABLE_IMAGE && layer instanceof ol.layer.Image) { + return new ol.renderer.webgl.ImageLayer(this, layer); + } else if (ol.ENABLE_TILE && layer instanceof ol.layer.Tile) { + return new ol.renderer.webgl.TileLayer(this, layer); + } else if (ol.ENABLE_VECTOR && layer instanceof ol.layer.Vector) { + return new ol.renderer.webgl.VectorLayer(this, layer); + } else { + goog.asserts.fail('unexpected layer configuration'); + return null; + } +}; + + +/** + * @param {ol.render.EventType} type Event type. + * @param {olx.FrameState} frameState Frame state. + * @private + */ +ol.renderer.webgl.Map.prototype.dispatchComposeEvent_ = function(type, frameState) { + var map = this.getMap(); + if (map.hasListener(type)) { + var context = this.context_; + + var extent = frameState.extent; + var size = frameState.size; + var viewState = frameState.viewState; + var pixelRatio = frameState.pixelRatio; + + var resolution = viewState.resolution; + var center = viewState.center; + var rotation = viewState.rotation; + + var vectorContext = new ol.render.webgl.Immediate(context, + center, resolution, rotation, size, extent, pixelRatio); + var composeEvent = new ol.render.Event(type, map, vectorContext, + frameState, null, context); + map.dispatchEvent(composeEvent); + } +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.disposeInternal = function() { + var gl = this.getGL(); + if (!gl.isContextLost()) { + this.textureCache_.forEach( + /** + * @param {?ol.WebglTextureCacheEntry} textureCacheEntry + * Texture cache entry. + */ + function(textureCacheEntry) { + if (textureCacheEntry) { + gl.deleteTexture(textureCacheEntry.texture); + } + }); + } + this.context_.dispose(); + ol.renderer.Map.prototype.disposeInternal.call(this); +}; + + +/** + * @param {ol.Map} map Map. + * @param {olx.FrameState} frameState Frame state. + * @private + */ +ol.renderer.webgl.Map.prototype.expireCache_ = function(map, frameState) { + var gl = this.getGL(); + var textureCacheEntry; + while (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > + ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { + textureCacheEntry = this.textureCache_.peekLast(); + if (!textureCacheEntry) { + if (+this.textureCache_.peekLastKey() == frameState.index) { + break; + } else { + --this.textureCacheFrameMarkerCount_; + } + } else { + gl.deleteTexture(textureCacheEntry.texture); + } + this.textureCache_.pop(); + } +}; + + +/** + * @return {ol.webgl.Context} The context. + */ +ol.renderer.webgl.Map.prototype.getContext = function() { + return this.context_; +}; + + +/** + * @return {WebGLRenderingContext} GL. + */ +ol.renderer.webgl.Map.prototype.getGL = function() { + return this.gl_; +}; + + +/** + * @return {ol.structs.PriorityQueue.<Array>} Tile texture queue. + */ +ol.renderer.webgl.Map.prototype.getTileTextureQueue = function() { + return this.tileTextureQueue_; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.getType = function() { + return ol.RendererType.WEBGL; +}; + + +/** + * @param {ol.events.Event} event Event. + * @protected + */ +ol.renderer.webgl.Map.prototype.handleWebGLContextLost = function(event) { + event.preventDefault(); + this.textureCache_.clear(); + this.textureCacheFrameMarkerCount_ = 0; + + var renderers = this.getLayerRenderers(); + for (var id in renderers) { + var renderer = /** @type {ol.renderer.webgl.Layer} */ (renderers[id]); + renderer.handleWebGLContextLost(); + } +}; + + +/** + * @protected + */ +ol.renderer.webgl.Map.prototype.handleWebGLContextRestored = function() { + this.initializeGL_(); + this.getMap().render(); +}; + + +/** + * @private + */ +ol.renderer.webgl.Map.prototype.initializeGL_ = function() { + var gl = this.gl_; + gl.activeTexture(goog.webgl.TEXTURE0); + gl.blendFuncSeparate( + goog.webgl.SRC_ALPHA, goog.webgl.ONE_MINUS_SRC_ALPHA, + goog.webgl.ONE, goog.webgl.ONE_MINUS_SRC_ALPHA); + gl.disable(goog.webgl.CULL_FACE); + gl.disable(goog.webgl.DEPTH_TEST); + gl.disable(goog.webgl.SCISSOR_TEST); + gl.disable(goog.webgl.STENCIL_TEST); +}; + + +/** + * @param {ol.Tile} tile Tile. + * @return {boolean} Is tile texture loaded. + */ +ol.renderer.webgl.Map.prototype.isTileTextureLoaded = function(tile) { + return this.textureCache_.containsKey(tile.getKey()); +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.renderFrame = function(frameState) { + + var context = this.getContext(); + var gl = this.getGL(); + + if (gl.isContextLost()) { + return false; + } + + if (!frameState) { + if (this.renderedVisible_) { + this.canvas_.style.display = 'none'; + this.renderedVisible_ = false; + } + return false; + } + + this.focus_ = frameState.focus; + + this.textureCache_.set((-frameState.index).toString(), null); + ++this.textureCacheFrameMarkerCount_; + + this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, frameState); + + /** @type {Array.<ol.LayerState>} */ + var layerStatesToDraw = []; + var layerStatesArray = frameState.layerStatesArray; + ol.array.stableSort(layerStatesArray, ol.renderer.Map.sortByZIndex); + + var viewResolution = frameState.viewState.resolution; + var i, ii, layerRenderer, layerState; + for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerState = layerStatesArray[i]; + if (ol.layer.Layer.visibleAtResolution(layerState, viewResolution) && + layerState.sourceState == ol.source.State.READY) { + layerRenderer = this.getLayerRenderer(layerState.layer); + goog.asserts.assertInstanceof(layerRenderer, ol.renderer.webgl.Layer, + 'renderer is an instance of ol.renderer.webgl.Layer'); + if (layerRenderer.prepareFrame(frameState, layerState, context)) { + layerStatesToDraw.push(layerState); + } + } + } + + var width = frameState.size[0] * frameState.pixelRatio; + var height = frameState.size[1] * frameState.pixelRatio; + if (this.canvas_.width != width || this.canvas_.height != height) { + this.canvas_.width = width; + this.canvas_.height = height; + } + + gl.bindFramebuffer(goog.webgl.FRAMEBUFFER, null); + + gl.clearColor(0, 0, 0, 0); + gl.clear(goog.webgl.COLOR_BUFFER_BIT); + gl.enable(goog.webgl.BLEND); + gl.viewport(0, 0, this.canvas_.width, this.canvas_.height); + + for (i = 0, ii = layerStatesToDraw.length; i < ii; ++i) { + layerState = layerStatesToDraw[i]; + layerRenderer = this.getLayerRenderer(layerState.layer); + goog.asserts.assertInstanceof(layerRenderer, ol.renderer.webgl.Layer, + 'renderer is an instance of ol.renderer.webgl.Layer'); + layerRenderer.composeFrame(frameState, layerState, context); + } + + if (!this.renderedVisible_) { + this.canvas_.style.display = ''; + this.renderedVisible_ = true; + } + + this.calculateMatrices2D(frameState); + + if (this.textureCache_.getCount() - this.textureCacheFrameMarkerCount_ > + ol.WEBGL_TEXTURE_CACHE_HIGH_WATER_MARK) { + frameState.postRenderFunctions.push( + /** @type {ol.PostRenderFunction} */ (this.expireCache_.bind(this)) + ); + } + + if (!this.tileTextureQueue_.isEmpty()) { + frameState.postRenderFunctions.push(this.loadNextTileTexture_); + frameState.animate = true; + } + + this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, frameState); + + this.scheduleRemoveUnusedLayerRenderers(frameState); + this.scheduleExpireIconCache(frameState); + +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.forEachFeatureAtCoordinate = function(coordinate, frameState, callback, thisArg, + layerFilter, thisArg2) { + var result; + + if (this.getGL().isContextLost()) { + return false; + } + + var viewState = frameState.viewState; + + var layerStates = frameState.layerStatesArray; + var numLayers = layerStates.length; + var i; + for (i = numLayers - 1; i >= 0; --i) { + var layerState = layerStates[i]; + var layer = layerState.layer; + if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) && + layerFilter.call(thisArg2, layer)) { + var layerRenderer = this.getLayerRenderer(layer); + result = layerRenderer.forEachFeatureAtCoordinate( + coordinate, frameState, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.hasFeatureAtCoordinate = function(coordinate, frameState, layerFilter, thisArg) { + var hasFeature = false; + + if (this.getGL().isContextLost()) { + return false; + } + + var viewState = frameState.viewState; + + var layerStates = frameState.layerStatesArray; + var numLayers = layerStates.length; + var i; + for (i = numLayers - 1; i >= 0; --i) { + var layerState = layerStates[i]; + var layer = layerState.layer; + if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) && + layerFilter.call(thisArg, layer)) { + var layerRenderer = this.getLayerRenderer(layer); + hasFeature = + layerRenderer.hasFeatureAtCoordinate(coordinate, frameState); + if (hasFeature) { + return true; + } + } + } + return hasFeature; +}; + + +/** + * @inheritDoc + */ +ol.renderer.webgl.Map.prototype.forEachLayerAtPixel = function(pixel, frameState, callback, thisArg, + layerFilter, thisArg2) { + if (this.getGL().isContextLost()) { + return false; + } + + var viewState = frameState.viewState; + var result; + + var layerStates = frameState.layerStatesArray; + var numLayers = layerStates.length; + var i; + for (i = numLayers - 1; i >= 0; --i) { + var layerState = layerStates[i]; + var layer = layerState.layer; + if (ol.layer.Layer.visibleAtResolution(layerState, viewState.resolution) && + layerFilter.call(thisArg, layer)) { + var layerRenderer = this.getLayerRenderer(layer); + result = layerRenderer.forEachLayerAtPixel( + pixel, frameState, callback, thisArg); + if (result) { + return result; + } + } + } + return undefined; +}; + +// FIXME recheck layer/map projection compatibility when projection changes +// FIXME layer renderers should skip when they can't reproject +// FIXME add tilt and height? + +goog.provide('ol.Map'); +goog.provide('ol.MapProperty'); + +goog.require('goog.asserts'); +goog.require('goog.async.nextTick'); +goog.require('goog.vec.Mat4'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEventType'); +goog.require('ol.MapBrowserEvent'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.MapBrowserEventHandler'); +goog.require('ol.MapEvent'); +goog.require('ol.MapEventType'); +goog.require('ol.Object'); +goog.require('ol.ObjectEvent'); +goog.require('ol.ObjectEventType'); +goog.require('ol.RendererType'); +goog.require('ol.TileQueue'); +goog.require('ol.View'); +goog.require('ol.ViewHint'); +goog.require('ol.array'); +goog.require('ol.control'); +goog.require('ol.dom'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.functions'); +goog.require('ol.has'); +goog.require('ol.interaction'); +goog.require('ol.layer.Base'); +goog.require('ol.layer.Group'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.proj.common'); +goog.require('ol.renderer.Map'); +goog.require('ol.renderer.canvas.Map'); +goog.require('ol.renderer.dom.Map'); +goog.require('ol.renderer.webgl.Map'); +goog.require('ol.size'); +goog.require('ol.structs.PriorityQueue'); +goog.require('ol.vec.Mat4'); + + +/** + * @const + * @type {string} + */ +ol.OL3_URL = 'http://openlayers.org/'; + + +/** + * @const + * @type {string} + */ +ol.OL3_LOGO_URL = 'data:image/png;base64,' + + 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBI' + + 'WXMAAAHGAAABxgEXwfpGAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAA' + + 'AhNQTFRF////AP//AICAgP//AFVVQECA////K1VVSbbbYL/fJ05idsTYJFtbbcjbJllmZszW' + + 'WMTOIFhoHlNiZszTa9DdUcHNHlNlV8XRIVdiasrUHlZjIVZjaMnVH1RlIFRkH1RkH1ZlasvY' + + 'asvXVsPQH1VkacnVa8vWIVZjIFRjVMPQa8rXIVVkXsXRsNveIFVkIFZlIVVj3eDeh6GmbMvX' + + 'H1ZkIFRka8rWbMvXIFVkIFVjIFVkbMvWH1VjbMvWIFVlbcvWIFVla8vVIFVkbMvWbMvVH1Vk' + + 'bMvWIFVlbcvWIFVkbcvVbMvWjNPbIFVkU8LPwMzNIFVkbczWIFVkbsvWbMvXIFVkRnB8bcvW' + + '2+TkW8XRIFVkIlZlJVloJlpoKlxrLl9tMmJwOWd0Omh1RXF8TneCT3iDUHiDU8LPVMLPVcLP' + + 'VcPQVsPPVsPQV8PQWMTQWsTQW8TQXMXSXsXRX4SNX8bSYMfTYcfTYsfTY8jUZcfSZsnUaIqT' + + 'acrVasrVa8jTa8rWbI2VbMvWbcvWdJObdcvUdszUd8vVeJaee87Yfc3WgJyjhqGnitDYjaar' + + 'ldPZnrK2oNbborW5o9bbo9fbpLa6q9ndrL3ArtndscDDutzfu8fJwN7gwt7gxc/QyuHhy+Hi' + + 'zeHi0NfX0+Pj19zb1+Tj2uXk29/e3uLg3+Lh3+bl4uXj4ufl4+fl5Ofl5ufl5ujm5+jmySDn' + + 'BAAAAFp0Uk5TAAECAgMEBAYHCA0NDg4UGRogIiMmKSssLzU7PkJJT1JTVFliY2hrdHZ3foSF' + + 'hYeJjY2QkpugqbG1tre5w8zQ09XY3uXn6+zx8vT09vf4+Pj5+fr6/P39/f3+gz7SsAAAAVVJ' + + 'REFUOMtjYKA7EBDnwCPLrObS1BRiLoJLnte6CQy8FLHLCzs2QUG4FjZ5GbcmBDDjxJBXDWxC' + + 'Brb8aM4zbkIDzpLYnAcE9VXlJSWlZRU13koIeW57mGx5XjoMZEUqwxWYQaQbSzLSkYGfKFSe' + + '0QMsX5WbjgY0YS4MBplemI4BdGBW+DQ11eZiymfqQuXZIjqwyadPNoSZ4L+0FVM6e+oGI6g8' + + 'a9iKNT3o8kVzNkzRg5lgl7p4wyRUL9Yt2jAxVh6mQCogae6GmflI8p0r13VFWTHBQ0rWPW7a' + + 'hgWVcPm+9cuLoyy4kCJDzCm6d8PSFoh0zvQNC5OjDJhQopPPJqph1doJBUD5tnkbZiUEqaCn' + + 'B3bTqLTFG1bPn71kw4b+GFdpLElKIzRxxgYgWNYc5SCENVHKeUaltHdXx0dZ8uBI1hJ2UUDg' + + 'q82CM2MwKeibqAvSO7MCABq0wXEPiqWEAAAAAElFTkSuQmCC'; + + +/** + * @type {Array.<ol.RendererType>} + * @const + */ +ol.DEFAULT_RENDERER_TYPES = [ + ol.RendererType.CANVAS, + ol.RendererType.WEBGL, + ol.RendererType.DOM +]; + + +/** + * @enum {string} + */ +ol.MapProperty = { + LAYERGROUP: 'layergroup', + SIZE: 'size', + TARGET: 'target', + VIEW: 'view' +}; + + +/** + * @classdesc + * The map is the core component of OpenLayers. For a map to render, a view, + * one or more layers, and a target container are needed: + * + * var map = new ol.Map({ + * view: new ol.View({ + * center: [0, 0], + * zoom: 1 + * }), + * layers: [ + * new ol.layer.Tile({ + * source: new ol.source.OSM() + * }) + * ], + * target: 'map' + * }); + * + * The above snippet creates a map using a {@link ol.layer.Tile} to display + * {@link ol.source.OSM} OSM data and render it to a DOM element with the + * id `map`. + * + * The constructor places a viewport container (with CSS class name + * `ol-viewport`) in the target element (see `getViewport()`), and then two + * further elements within the viewport: one with CSS class name + * `ol-overlaycontainer-stopevent` for controls and some overlays, and one with + * CSS class name `ol-overlaycontainer` for other overlays (see the `stopEvent` + * option of {@link ol.Overlay} for the difference). The map itself is placed in + * a further element within the viewport, either DOM or Canvas, depending on the + * renderer. + * + * Layers are stored as a `ol.Collection` in layerGroups. A top-level group is + * provided by the library. This is what is accessed by `getLayerGroup` and + * `setLayerGroup`. Layers entered in the options are added to this group, and + * `addLayer` and `removeLayer` change the layer collection in the group. + * `getLayers` is a convenience function for `getLayerGroup().getLayers()`. + * Note that `ol.layer.Group` is a subclass of `ol.layer.Base`, so layers + * entered in the options or added with `addLayer` can be groups, which can + * contain further groups, and so on. + * + * @constructor + * @extends {ol.Object} + * @param {olx.MapOptions} options Map options. + * @fires ol.MapBrowserEvent + * @fires ol.MapEvent + * @fires ol.render.Event#postcompose + * @fires ol.render.Event#precompose + * @api stable + */ +ol.Map = function(options) { + + ol.Object.call(this); + + var optionsInternal = ol.Map.createOptionsInternal(options); + + /** + * @type {boolean} + * @private + */ + this.loadTilesWhileAnimating_ = + options.loadTilesWhileAnimating !== undefined ? + options.loadTilesWhileAnimating : false; + + /** + * @type {boolean} + * @private + */ + this.loadTilesWhileInteracting_ = + options.loadTilesWhileInteracting !== undefined ? + options.loadTilesWhileInteracting : false; + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = options.pixelRatio !== undefined ? + options.pixelRatio : ol.has.DEVICE_PIXEL_RATIO; + + /** + * @private + * @type {Object.<string, string>} + */ + this.logos_ = optionsInternal.logos; + + /** + * @private + * @type {number|undefined} + */ + this.animationDelayKey_; + + /** + * @private + */ + this.animationDelay_ = function() { + this.animationDelayKey_ = undefined; + this.renderFrame_.call(this, Date.now()); + }.bind(this); + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.coordinateToPixelMatrix_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {goog.vec.Mat4.Number} + */ + this.pixelToCoordinateMatrix_ = goog.vec.Mat4.createNumber(); + + /** + * @private + * @type {number} + */ + this.frameIndex_ = 0; + + /** + * @private + * @type {?olx.FrameState} + */ + this.frameState_ = null; + + /** + * The extent at the previous 'moveend' event. + * @private + * @type {ol.Extent} + */ + this.previousExtent_ = ol.extent.createEmpty(); + + /** + * @private + * @type {?ol.EventsKey} + */ + this.viewPropertyListenerKey_ = null; + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.layerGroupPropertyListenerKeys_ = null; + + /** + * @private + * @type {Element} + */ + this.viewport_ = document.createElement('DIV'); + this.viewport_.className = 'ol-viewport' + (ol.has.TOUCH ? ' ol-touch' : ''); + this.viewport_.style.position = 'relative'; + this.viewport_.style.overflow = 'hidden'; + this.viewport_.style.width = '100%'; + this.viewport_.style.height = '100%'; + // prevent page zoom on IE >= 10 browsers + this.viewport_.style.msTouchAction = 'none'; + this.viewport_.style.touchAction = 'none'; + + /** + * @private + * @type {!Element} + */ + this.overlayContainer_ = document.createElement('DIV'); + this.overlayContainer_.className = 'ol-overlaycontainer'; + this.viewport_.appendChild(this.overlayContainer_); + + /** + * @private + * @type {!Element} + */ + this.overlayContainerStopEvent_ = document.createElement('DIV'); + this.overlayContainerStopEvent_.className = 'ol-overlaycontainer-stopevent'; + var overlayEvents = [ + ol.events.EventType.CLICK, + ol.events.EventType.DBLCLICK, + ol.events.EventType.MOUSEDOWN, + ol.events.EventType.TOUCHSTART, + ol.events.EventType.MSPOINTERDOWN, + ol.MapBrowserEvent.EventType.POINTERDOWN, + ol.events.EventType.MOUSEWHEEL, + ol.events.EventType.WHEEL + ]; + for (var i = 0, ii = overlayEvents.length; i < ii; ++i) { + ol.events.listen(this.overlayContainerStopEvent_, overlayEvents[i], + ol.events.Event.stopPropagation); + } + this.viewport_.appendChild(this.overlayContainerStopEvent_); + + /** + * @private + * @type {ol.MapBrowserEventHandler} + */ + this.mapBrowserEventHandler_ = new ol.MapBrowserEventHandler(this); + for (var key in ol.MapBrowserEvent.EventType) { + ol.events.listen(this.mapBrowserEventHandler_, ol.MapBrowserEvent.EventType[key], + this.handleMapBrowserEvent, this); + } + + /** + * @private + * @type {Element|Document} + */ + this.keyboardEventTarget_ = optionsInternal.keyboardEventTarget; + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.keyHandlerKeys_ = null; + + ol.events.listen(this.viewport_, ol.events.EventType.WHEEL, + this.handleBrowserEvent, this); + ol.events.listen(this.viewport_, ol.events.EventType.MOUSEWHEEL, + this.handleBrowserEvent, this); + + /** + * @type {ol.Collection.<ol.control.Control>} + * @private + */ + this.controls_ = optionsInternal.controls; + + /** + * @type {ol.Collection.<ol.interaction.Interaction>} + * @private + */ + this.interactions_ = optionsInternal.interactions; + + /** + * @type {ol.Collection.<ol.Overlay>} + * @private + */ + this.overlays_ = optionsInternal.overlays; + + /** + * A lookup of overlays by id. + * @private + * @type {Object.<string, ol.Overlay>} + */ + this.overlayIdIndex_ = {}; + + /** + * @type {ol.renderer.Map} + * @private + */ + this.renderer_ = new optionsInternal.rendererConstructor(this.viewport_, this); + + /** + * @type {function(Event)|undefined} + * @private + */ + this.handleResize_; + + /** + * @private + * @type {ol.Coordinate} + */ + this.focus_ = null; + + /** + * @private + * @type {Array.<ol.PreRenderFunction>} + */ + this.preRenderFunctions_ = []; + + /** + * @private + * @type {Array.<ol.PostRenderFunction>} + */ + this.postRenderFunctions_ = []; + + /** + * @private + * @type {ol.TileQueue} + */ + this.tileQueue_ = new ol.TileQueue( + this.getTilePriority.bind(this), + this.handleTileChange_.bind(this)); + + /** + * Uids of features to skip at rendering time. + * @type {Object.<string, boolean>} + * @private + */ + this.skippedFeatureUids_ = {}; + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.MapProperty.LAYERGROUP), + this.handleLayerGroupChanged_, this); + ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.VIEW), + this.handleViewChanged_, this); + ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.SIZE), + this.handleSizeChanged_, this); + ol.events.listen(this, ol.Object.getChangeEventType(ol.MapProperty.TARGET), + this.handleTargetChanged_, this); + + // setProperties will trigger the rendering of the map if the map + // is "defined" already. + this.setProperties(optionsInternal.values); + + this.controls_.forEach( + /** + * @param {ol.control.Control} control Control. + * @this {ol.Map} + */ + function(control) { + control.setMap(this); + }, this); + + ol.events.listen(this.controls_, ol.CollectionEventType.ADD, + /** + * @param {ol.CollectionEvent} event Collection event. + */ + function(event) { + event.element.setMap(this); + }, this); + + ol.events.listen(this.controls_, ol.CollectionEventType.REMOVE, + /** + * @param {ol.CollectionEvent} event Collection event. + */ + function(event) { + event.element.setMap(null); + }, this); + + this.interactions_.forEach( + /** + * @param {ol.interaction.Interaction} interaction Interaction. + * @this {ol.Map} + */ + function(interaction) { + interaction.setMap(this); + }, this); + + ol.events.listen(this.interactions_, ol.CollectionEventType.ADD, + /** + * @param {ol.CollectionEvent} event Collection event. + */ + function(event) { + event.element.setMap(this); + }, this); + + ol.events.listen(this.interactions_, ol.CollectionEventType.REMOVE, + /** + * @param {ol.CollectionEvent} event Collection event. + */ + function(event) { + event.element.setMap(null); + }, this); + + this.overlays_.forEach(this.addOverlayInternal_, this); + + ol.events.listen(this.overlays_, ol.CollectionEventType.ADD, + /** + * @param {ol.CollectionEvent} event Collection event. + */ + function(event) { + this.addOverlayInternal_(/** @type {ol.Overlay} */ (event.element)); + }, this); + + ol.events.listen(this.overlays_, ol.CollectionEventType.REMOVE, + /** + * @param {ol.CollectionEvent} event Collection event. + */ + function(event) { + var id = event.element.getId(); + if (id !== undefined) { + delete this.overlayIdIndex_[id.toString()]; + } + event.element.setMap(null); + }, this); + +}; +ol.inherits(ol.Map, ol.Object); + + +/** + * Add the given control to the map. + * @param {ol.control.Control} control Control. + * @api stable + */ +ol.Map.prototype.addControl = function(control) { + var controls = this.getControls(); + goog.asserts.assert(controls !== undefined, 'controls should be defined'); + controls.push(control); +}; + + +/** + * Add the given interaction to the map. + * @param {ol.interaction.Interaction} interaction Interaction to add. + * @api stable + */ +ol.Map.prototype.addInteraction = function(interaction) { + var interactions = this.getInteractions(); + goog.asserts.assert(interactions !== undefined, + 'interactions should be defined'); + interactions.push(interaction); +}; + + +/** + * Adds the given layer to the top of this map. If you want to add a layer + * elsewhere in the stack, use `getLayers()` and the methods available on + * {@link ol.Collection}. + * @param {ol.layer.Base} layer Layer. + * @api stable + */ +ol.Map.prototype.addLayer = function(layer) { + var layers = this.getLayerGroup().getLayers(); + layers.push(layer); +}; + + +/** + * Add the given overlay to the map. + * @param {ol.Overlay} overlay Overlay. + * @api stable + */ +ol.Map.prototype.addOverlay = function(overlay) { + var overlays = this.getOverlays(); + goog.asserts.assert(overlays !== undefined, 'overlays should be defined'); + overlays.push(overlay); +}; + + +/** + * This deals with map's overlay collection changes. + * @param {ol.Overlay} overlay Overlay. + * @private + */ +ol.Map.prototype.addOverlayInternal_ = function(overlay) { + var id = overlay.getId(); + if (id !== undefined) { + this.overlayIdIndex_[id.toString()] = overlay; + } + overlay.setMap(this); +}; + + +/** + * Add functions to be called before rendering. This can be used for attaching + * animations before updating the map's view. The {@link ol.animation} + * namespace provides several static methods for creating prerender functions. + * @param {...ol.PreRenderFunction} var_args Any number of pre-render functions. + * @api + */ +ol.Map.prototype.beforeRender = function(var_args) { + this.render(); + Array.prototype.push.apply(this.preRenderFunctions_, arguments); +}; + + +/** + * @param {ol.PreRenderFunction} preRenderFunction Pre-render function. + * @return {boolean} Whether the preRenderFunction has been found and removed. + */ +ol.Map.prototype.removePreRenderFunction = function(preRenderFunction) { + return ol.array.remove(this.preRenderFunctions_, preRenderFunction); +}; + + +/** + * + * @inheritDoc + */ +ol.Map.prototype.disposeInternal = function() { + this.mapBrowserEventHandler_.dispose(); + this.renderer_.dispose(); + ol.events.unlisten(this.viewport_, ol.events.EventType.WHEEL, + this.handleBrowserEvent, this); + ol.events.unlisten(this.viewport_, ol.events.EventType.MOUSEWHEEL, + this.handleBrowserEvent, this); + if (this.handleResize_ !== undefined) { + ol.global.removeEventListener(ol.events.EventType.RESIZE, + this.handleResize_, false); + this.handleResize_ = undefined; + } + if (this.animationDelayKey_) { + ol.global.cancelAnimationFrame(this.animationDelayKey_); + this.animationDelayKey_ = undefined; + } + this.setTarget(null); + ol.Object.prototype.disposeInternal.call(this); +}; + + +/** + * Detect features that intersect a pixel on the viewport, and execute a + * callback with each intersecting feature. Layers included in the detection can + * be configured through `opt_layerFilter`. + * @param {ol.Pixel} pixel Pixel. + * @param {function(this: S, (ol.Feature|ol.render.Feature), + * ol.layer.Layer): T} callback Feature callback. The callback will be + * called with two arguments. The first argument is one + * {@link ol.Feature feature} or + * {@link ol.render.Feature render feature} at the pixel, the second is + * the {@link ol.layer.Layer layer} of the feature and will be null for + * unmanaged layers. To stop detection, callback functions can return a + * truthy value. + * @param {S=} opt_this Value to use as `this` when executing `callback`. + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer + * filter function. The filter function will receive one argument, the + * {@link ol.layer.Layer layer-candidate} and it should return a boolean + * value. Only layers which are visible and for which this function returns + * `true` will be tested for features. By default, all visible layers will + * be tested. + * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result, i.e. the return value of last + * callback execution, or the first truthy callback return value. + * @template S,T,U + * @api stable + */ +ol.Map.prototype.forEachFeatureAtPixel = function(pixel, callback, opt_this, opt_layerFilter, opt_this2) { + if (!this.frameState_) { + return; + } + var coordinate = this.getCoordinateFromPixel(pixel); + var thisArg = opt_this !== undefined ? opt_this : null; + var layerFilter = opt_layerFilter !== undefined ? + opt_layerFilter : ol.functions.TRUE; + var thisArg2 = opt_this2 !== undefined ? opt_this2 : null; + return this.renderer_.forEachFeatureAtCoordinate( + coordinate, this.frameState_, callback, thisArg, + layerFilter, thisArg2); +}; + + +/** + * Detect layers that have a color value at a pixel on the viewport, and + * execute a callback with each matching layer. Layers included in the + * detection can be configured through `opt_layerFilter`. + * @param {ol.Pixel} pixel Pixel. + * @param {function(this: S, ol.layer.Layer): T} callback Layer + * callback. Will receive one argument, the {@link ol.layer.Layer layer} + * that contains the color pixel. To stop detection, callback functions can + * return a truthy value. + * @param {S=} opt_this Value to use as `this` when executing `callback`. + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer + * filter function. The filter function will receive one argument, the + * {@link ol.layer.Layer layer-candidate} and it should return a boolean + * value. Only layers which are visible and for which this function returns + * `true` will be tested for features. By default, all visible layers will + * be tested. + * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`. + * @return {T|undefined} Callback result, i.e. the return value of last + * callback execution, or the first truthy callback return value. + * @template S,T,U + * @api stable + */ +ol.Map.prototype.forEachLayerAtPixel = function(pixel, callback, opt_this, opt_layerFilter, opt_this2) { + if (!this.frameState_) { + return; + } + var thisArg = opt_this !== undefined ? opt_this : null; + var layerFilter = opt_layerFilter !== undefined ? + opt_layerFilter : ol.functions.TRUE; + var thisArg2 = opt_this2 !== undefined ? opt_this2 : null; + return this.renderer_.forEachLayerAtPixel( + pixel, this.frameState_, callback, thisArg, + layerFilter, thisArg2); +}; + + +/** + * Detect if features intersect a pixel on the viewport. Layers included in the + * detection can be configured through `opt_layerFilter`. + * @param {ol.Pixel} pixel Pixel. + * @param {(function(this: U, ol.layer.Layer): boolean)=} opt_layerFilter Layer + * filter function. The filter function will receive one argument, the + * {@link ol.layer.Layer layer-candidate} and it should return a boolean + * value. Only layers which are visible and for which this function returns + * `true` will be tested for features. By default, all visible layers will + * be tested. + * @param {U=} opt_this Value to use as `this` when executing `layerFilter`. + * @return {boolean} Is there a feature at the given pixel? + * @template U + * @api + */ +ol.Map.prototype.hasFeatureAtPixel = function(pixel, opt_layerFilter, opt_this) { + if (!this.frameState_) { + return false; + } + var coordinate = this.getCoordinateFromPixel(pixel); + var layerFilter = opt_layerFilter !== undefined ? + opt_layerFilter : ol.functions.TRUE; + var thisArg = opt_this !== undefined ? opt_this : null; + return this.renderer_.hasFeatureAtCoordinate( + coordinate, this.frameState_, layerFilter, thisArg); +}; + + +/** + * Returns the geographical coordinate for a browser event. + * @param {Event} event Event. + * @return {ol.Coordinate} Coordinate. + * @api stable + */ +ol.Map.prototype.getEventCoordinate = function(event) { + return this.getCoordinateFromPixel(this.getEventPixel(event)); +}; + + +/** + * Returns the map pixel position for a browser event relative to the viewport. + * @param {Event} event Event. + * @return {ol.Pixel} Pixel. + * @api stable + */ +ol.Map.prototype.getEventPixel = function(event) { + var viewportPosition = this.viewport_.getBoundingClientRect(); + var eventPosition = event.changedTouches ? event.changedTouches[0] : event; + return [ + eventPosition.clientX - viewportPosition.left, + eventPosition.clientY - viewportPosition.top + ]; +}; + + +/** + * Get the target in which this map is rendered. + * Note that this returns what is entered as an option or in setTarget: + * if that was an element, it returns an element; if a string, it returns that. + * @return {Element|string|undefined} The Element or id of the Element that the + * map is rendered in. + * @observable + * @api stable + */ +ol.Map.prototype.getTarget = function() { + return /** @type {Element|string|undefined} */ ( + this.get(ol.MapProperty.TARGET)); +}; + + +/** + * Get the DOM element into which this map is rendered. In contrast to + * `getTarget` this method always return an `Element`, or `null` if the + * map has no target. + * @return {Element} The element that the map is rendered in. + * @api + */ +ol.Map.prototype.getTargetElement = function() { + var target = this.getTarget(); + if (target !== undefined) { + return typeof target === 'string' ? + document.getElementById(target) : + target; + } else { + return null; + } +}; + + +/** + * Get the coordinate for a given pixel. This returns a coordinate in the + * map view projection. + * @param {ol.Pixel} pixel Pixel position in the map viewport. + * @return {ol.Coordinate} The coordinate for the pixel position. + * @api stable + */ +ol.Map.prototype.getCoordinateFromPixel = function(pixel) { + var frameState = this.frameState_; + if (!frameState) { + return null; + } else { + var vec2 = pixel.slice(); + return ol.vec.Mat4.multVec2(frameState.pixelToCoordinateMatrix, vec2, vec2); + } +}; + + +/** + * Get the map controls. Modifying this collection changes the controls + * associated with the map. + * @return {ol.Collection.<ol.control.Control>} Controls. + * @api stable + */ +ol.Map.prototype.getControls = function() { + return this.controls_; +}; + + +/** + * Get the map overlays. Modifying this collection changes the overlays + * associated with the map. + * @return {ol.Collection.<ol.Overlay>} Overlays. + * @api stable + */ +ol.Map.prototype.getOverlays = function() { + return this.overlays_; +}; + + +/** + * Get an overlay by its identifier (the value returned by overlay.getId()). + * Note that the index treats string and numeric identifiers as the same. So + * `map.getOverlayById(2)` will return an overlay with id `'2'` or `2`. + * @param {string|number} id Overlay identifier. + * @return {ol.Overlay} Overlay. + * @api + */ +ol.Map.prototype.getOverlayById = function(id) { + var overlay = this.overlayIdIndex_[id.toString()]; + return overlay !== undefined ? overlay : null; +}; + + +/** + * Get the map interactions. Modifying this collection changes the interactions + * associated with the map. + * + * Interactions are used for e.g. pan, zoom and rotate. + * @return {ol.Collection.<ol.interaction.Interaction>} Interactions. + * @api stable + */ +ol.Map.prototype.getInteractions = function() { + return this.interactions_; +}; + + +/** + * Get the layergroup associated with this map. + * @return {ol.layer.Group} A layer group containing the layers in this map. + * @observable + * @api stable + */ +ol.Map.prototype.getLayerGroup = function() { + return /** @type {ol.layer.Group} */ (this.get(ol.MapProperty.LAYERGROUP)); +}; + + +/** + * Get the collection of layers associated with this map. + * @return {!ol.Collection.<ol.layer.Base>} Layers. + * @api stable + */ +ol.Map.prototype.getLayers = function() { + var layers = this.getLayerGroup().getLayers(); + return layers; +}; + + +/** + * Get the pixel for a coordinate. This takes a coordinate in the map view + * projection and returns the corresponding pixel. + * @param {ol.Coordinate} coordinate A map coordinate. + * @return {ol.Pixel} A pixel position in the map viewport. + * @api stable + */ +ol.Map.prototype.getPixelFromCoordinate = function(coordinate) { + var frameState = this.frameState_; + if (!frameState) { + return null; + } else { + var vec2 = coordinate.slice(0, 2); + return ol.vec.Mat4.multVec2(frameState.coordinateToPixelMatrix, vec2, vec2); + } +}; + + +/** + * Get the map renderer. + * @return {ol.renderer.Map} Renderer + */ +ol.Map.prototype.getRenderer = function() { + return this.renderer_; +}; + + +/** + * Get the size of this map. + * @return {ol.Size|undefined} The size in pixels of the map in the DOM. + * @observable + * @api stable + */ +ol.Map.prototype.getSize = function() { + return /** @type {ol.Size|undefined} */ (this.get(ol.MapProperty.SIZE)); +}; + + +/** + * Get the view associated with this map. A view manages properties such as + * center and resolution. + * @return {ol.View} The view that controls this map. + * @observable + * @api stable + */ +ol.Map.prototype.getView = function() { + return /** @type {ol.View} */ (this.get(ol.MapProperty.VIEW)); +}; + + +/** + * Get the element that serves as the map viewport. + * @return {Element} Viewport. + * @api stable + */ +ol.Map.prototype.getViewport = function() { + return this.viewport_; +}; + + +/** + * Get the element that serves as the container for overlays. Elements added to + * this container will let mousedown and touchstart events through to the map, + * so clicks and gestures on an overlay will trigger {@link ol.MapBrowserEvent} + * events. + * @return {!Element} The map's overlay container. + */ +ol.Map.prototype.getOverlayContainer = function() { + return this.overlayContainer_; +}; + + +/** + * Get the element that serves as a container for overlays that don't allow + * event propagation. Elements added to this container won't let mousedown and + * touchstart events through to the map, so clicks and gestures on an overlay + * don't trigger any {@link ol.MapBrowserEvent}. + * @return {!Element} The map's overlay container that stops events. + */ +ol.Map.prototype.getOverlayContainerStopEvent = function() { + return this.overlayContainerStopEvent_; +}; + + +/** + * @param {ol.Tile} tile Tile. + * @param {string} tileSourceKey Tile source key. + * @param {ol.Coordinate} tileCenter Tile center. + * @param {number} tileResolution Tile resolution. + * @return {number} Tile priority. + */ +ol.Map.prototype.getTilePriority = function(tile, tileSourceKey, tileCenter, tileResolution) { + // Filter out tiles at higher zoom levels than the current zoom level, or that + // are outside the visible extent. + var frameState = this.frameState_; + if (!frameState || !(tileSourceKey in frameState.wantedTiles)) { + return ol.structs.PriorityQueue.DROP; + } + var coordKey = tile.tileCoord.toString(); + if (!frameState.wantedTiles[tileSourceKey][coordKey]) { + return ol.structs.PriorityQueue.DROP; + } + // Prioritize the highest zoom level tiles closest to the focus. + // Tiles at higher zoom levels are prioritized using Math.log(tileResolution). + // Within a zoom level, tiles are prioritized by the distance in pixels + // between the center of the tile and the focus. The factor of 65536 means + // that the prioritization should behave as desired for tiles up to + // 65536 * Math.log(2) = 45426 pixels from the focus. + var deltaX = tileCenter[0] - frameState.focus[0]; + var deltaY = tileCenter[1] - frameState.focus[1]; + return 65536 * Math.log(tileResolution) + + Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution; +}; + + +/** + * @param {Event} browserEvent Browser event. + * @param {string=} opt_type Type. + */ +ol.Map.prototype.handleBrowserEvent = function(browserEvent, opt_type) { + var type = opt_type || browserEvent.type; + var mapBrowserEvent = new ol.MapBrowserEvent(type, this, browserEvent); + this.handleMapBrowserEvent(mapBrowserEvent); +}; + + +/** + * @param {ol.MapBrowserEvent} mapBrowserEvent The event to handle. + */ +ol.Map.prototype.handleMapBrowserEvent = function(mapBrowserEvent) { + if (!this.frameState_) { + // With no view defined, we cannot translate pixels into geographical + // coordinates so interactions cannot be used. + return; + } + this.focus_ = mapBrowserEvent.coordinate; + mapBrowserEvent.frameState = this.frameState_; + var interactions = this.getInteractions(); + goog.asserts.assert(interactions !== undefined, + 'interactions should be defined'); + var interactionsArray = interactions.getArray(); + var i; + if (this.dispatchEvent(mapBrowserEvent) !== false) { + for (i = interactionsArray.length - 1; i >= 0; i--) { + var interaction = interactionsArray[i]; + if (!interaction.getActive()) { + continue; + } + var cont = interaction.handleEvent(mapBrowserEvent); + if (!cont) { + break; + } + } + } +}; + + +/** + * @protected + */ +ol.Map.prototype.handlePostRender = function() { + + var frameState = this.frameState_; + + // Manage the tile queue + // Image loads are expensive and a limited resource, so try to use them + // efficiently: + // * When the view is static we allow a large number of parallel tile loads + // to complete the frame as quickly as possible. + // * When animating or interacting, image loads can cause janks, so we reduce + // the maximum number of loads per frame and limit the number of parallel + // tile loads to remain reactive to view changes and to reduce the chance of + // loading tiles that will quickly disappear from view. + var tileQueue = this.tileQueue_; + if (!tileQueue.isEmpty()) { + var maxTotalLoading = 16; + var maxNewLoads = maxTotalLoading; + if (frameState) { + var hints = frameState.viewHints; + if (hints[ol.ViewHint.ANIMATING]) { + maxTotalLoading = this.loadTilesWhileAnimating_ ? 8 : 0; + maxNewLoads = 2; + } + if (hints[ol.ViewHint.INTERACTING]) { + maxTotalLoading = this.loadTilesWhileInteracting_ ? 8 : 0; + maxNewLoads = 2; + } + } + if (tileQueue.getTilesLoading() < maxTotalLoading) { + tileQueue.reprioritize(); // FIXME only call if view has changed + tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads); + } + } + + var postRenderFunctions = this.postRenderFunctions_; + var i, ii; + for (i = 0, ii = postRenderFunctions.length; i < ii; ++i) { + postRenderFunctions[i](this, frameState); + } + postRenderFunctions.length = 0; +}; + + +/** + * @private + */ +ol.Map.prototype.handleSizeChanged_ = function() { + this.render(); +}; + + +/** + * @private + */ +ol.Map.prototype.handleTargetChanged_ = function() { + // target may be undefined, null, a string or an Element. + // If it's a string we convert it to an Element before proceeding. + // If it's not now an Element we remove the viewport from the DOM. + // If it's an Element we append the viewport element to it. + + var targetElement; + if (this.getTarget()) { + targetElement = this.getTargetElement(); + goog.asserts.assert(targetElement !== null, + 'expects a non-null value for targetElement'); + } + + if (this.keyHandlerKeys_) { + for (var i = 0, ii = this.keyHandlerKeys_.length; i < ii; ++i) { + ol.events.unlistenByKey(this.keyHandlerKeys_[i]); + } + this.keyHandlerKeys_ = null; + } + + if (!targetElement) { + ol.dom.removeNode(this.viewport_); + if (this.handleResize_ !== undefined) { + ol.global.removeEventListener(ol.events.EventType.RESIZE, + this.handleResize_, false); + this.handleResize_ = undefined; + } + } else { + targetElement.appendChild(this.viewport_); + + var keyboardEventTarget = !this.keyboardEventTarget_ ? + targetElement : this.keyboardEventTarget_; + this.keyHandlerKeys_ = [ + ol.events.listen(keyboardEventTarget, ol.events.EventType.KEYDOWN, + this.handleBrowserEvent, this), + ol.events.listen(keyboardEventTarget, ol.events.EventType.KEYPRESS, + this.handleBrowserEvent, this) + ]; + + if (!this.handleResize_) { + this.handleResize_ = this.updateSize.bind(this); + ol.global.addEventListener(ol.events.EventType.RESIZE, + this.handleResize_, false); + } + } + + this.updateSize(); + // updateSize calls setSize, so no need to call this.render + // ourselves here. +}; + + +/** + * @private + */ +ol.Map.prototype.handleTileChange_ = function() { + this.render(); +}; + + +/** + * @private + */ +ol.Map.prototype.handleViewPropertyChanged_ = function() { + this.render(); +}; + + +/** + * @private + */ +ol.Map.prototype.handleViewChanged_ = function() { + if (this.viewPropertyListenerKey_) { + ol.events.unlistenByKey(this.viewPropertyListenerKey_); + this.viewPropertyListenerKey_ = null; + } + var view = this.getView(); + if (view) { + this.viewPropertyListenerKey_ = ol.events.listen( + view, ol.ObjectEventType.PROPERTYCHANGE, + this.handleViewPropertyChanged_, this); + } + this.render(); +}; + + +/** + * @param {ol.events.Event} event Event. + * @private + */ +ol.Map.prototype.handleLayerGroupMemberChanged_ = function(event) { + goog.asserts.assertInstanceof(event, ol.events.Event, + 'event should be an Event'); + this.render(); +}; + + +/** + * @param {ol.ObjectEvent} event Event. + * @private + */ +ol.Map.prototype.handleLayerGroupPropertyChanged_ = function(event) { + goog.asserts.assertInstanceof(event, ol.ObjectEvent, + 'event should be an ol.ObjectEvent'); + this.render(); +}; + + +/** + * @private + */ +ol.Map.prototype.handleLayerGroupChanged_ = function() { + if (this.layerGroupPropertyListenerKeys_) { + this.layerGroupPropertyListenerKeys_.forEach(ol.events.unlistenByKey); + this.layerGroupPropertyListenerKeys_ = null; + } + var layerGroup = this.getLayerGroup(); + if (layerGroup) { + this.layerGroupPropertyListenerKeys_ = [ + ol.events.listen( + layerGroup, ol.ObjectEventType.PROPERTYCHANGE, + this.handleLayerGroupPropertyChanged_, this), + ol.events.listen( + layerGroup, ol.events.EventType.CHANGE, + this.handleLayerGroupMemberChanged_, this) + ]; + } + this.render(); +}; + + +/** + * @return {boolean} Is rendered. + */ +ol.Map.prototype.isRendered = function() { + return !!this.frameState_; +}; + + +/** + * Requests an immediate render in a synchronous manner. + * @api stable + */ +ol.Map.prototype.renderSync = function() { + if (this.animationDelayKey_) { + ol.global.cancelAnimationFrame(this.animationDelayKey_); + } + this.animationDelay_(); +}; + + +/** + * Request a map rendering (at the next animation frame). + * @api stable + */ +ol.Map.prototype.render = function() { + if (this.animationDelayKey_ === undefined) { + this.animationDelayKey_ = ol.global.requestAnimationFrame( + this.animationDelay_); + } +}; + + +/** + * Remove the given control from the map. + * @param {ol.control.Control} control Control. + * @return {ol.control.Control|undefined} The removed control (or undefined + * if the control was not found). + * @api stable + */ +ol.Map.prototype.removeControl = function(control) { + var controls = this.getControls(); + goog.asserts.assert(controls !== undefined, 'controls should be defined'); + return controls.remove(control); +}; + + +/** + * Remove the given interaction from the map. + * @param {ol.interaction.Interaction} interaction Interaction to remove. + * @return {ol.interaction.Interaction|undefined} The removed interaction (or + * undefined if the interaction was not found). + * @api stable + */ +ol.Map.prototype.removeInteraction = function(interaction) { + var interactions = this.getInteractions(); + goog.asserts.assert(interactions !== undefined, + 'interactions should be defined'); + return interactions.remove(interaction); +}; + + +/** + * Removes the given layer from the map. + * @param {ol.layer.Base} layer Layer. + * @return {ol.layer.Base|undefined} The removed layer (or undefined if the + * layer was not found). + * @api stable + */ +ol.Map.prototype.removeLayer = function(layer) { + var layers = this.getLayerGroup().getLayers(); + return layers.remove(layer); +}; + + +/** + * Remove the given overlay from the map. + * @param {ol.Overlay} overlay Overlay. + * @return {ol.Overlay|undefined} The removed overlay (or undefined + * if the overlay was not found). + * @api stable + */ +ol.Map.prototype.removeOverlay = function(overlay) { + var overlays = this.getOverlays(); + goog.asserts.assert(overlays !== undefined, 'overlays should be defined'); + return overlays.remove(overlay); +}; + + +/** + * @param {number} time Time. + * @private + */ +ol.Map.prototype.renderFrame_ = function(time) { + + var i, ii, viewState; + + var size = this.getSize(); + var view = this.getView(); + var extent = ol.extent.createEmpty(); + /** @type {?olx.FrameState} */ + var frameState = null; + if (size !== undefined && ol.size.hasArea(size) && view && view.isDef()) { + var viewHints = view.getHints(this.frameState_ ? this.frameState_.viewHints : undefined); + var layerStatesArray = this.getLayerGroup().getLayerStatesArray(); + var layerStates = {}; + for (i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerStates[goog.getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; + } + viewState = view.getState(); + frameState = /** @type {olx.FrameState} */ ({ + animate: false, + attributions: {}, + coordinateToPixelMatrix: this.coordinateToPixelMatrix_, + extent: extent, + focus: !this.focus_ ? viewState.center : this.focus_, + index: this.frameIndex_++, + layerStates: layerStates, + layerStatesArray: layerStatesArray, + logos: ol.object.assign({}, this.logos_), + pixelRatio: this.pixelRatio_, + pixelToCoordinateMatrix: this.pixelToCoordinateMatrix_, + postRenderFunctions: [], + size: size, + skippedFeatureUids: this.skippedFeatureUids_, + tileQueue: this.tileQueue_, + time: time, + usedTiles: {}, + viewState: viewState, + viewHints: viewHints, + wantedTiles: {} + }); + } + + if (frameState) { + var preRenderFunctions = this.preRenderFunctions_; + var n = 0, preRenderFunction; + for (i = 0, ii = preRenderFunctions.length; i < ii; ++i) { + preRenderFunction = preRenderFunctions[i]; + if (preRenderFunction(this, frameState)) { + preRenderFunctions[n++] = preRenderFunction; + } + } + preRenderFunctions.length = n; + + frameState.extent = ol.extent.getForViewAndSize(viewState.center, + viewState.resolution, viewState.rotation, frameState.size, extent); + } + + this.frameState_ = frameState; + this.renderer_.renderFrame(frameState); + + if (frameState) { + if (frameState.animate) { + this.render(); + } + Array.prototype.push.apply( + this.postRenderFunctions_, frameState.postRenderFunctions); + + var idle = this.preRenderFunctions_.length === 0 && + !frameState.viewHints[ol.ViewHint.ANIMATING] && + !frameState.viewHints[ol.ViewHint.INTERACTING] && + !ol.extent.equals(frameState.extent, this.previousExtent_); + + if (idle) { + this.dispatchEvent( + new ol.MapEvent(ol.MapEventType.MOVEEND, this, frameState)); + ol.extent.clone(frameState.extent, this.previousExtent_); + } + } + + this.dispatchEvent( + new ol.MapEvent(ol.MapEventType.POSTRENDER, this, frameState)); + + goog.async.nextTick(this.handlePostRender, this); + +}; + + +/** + * Sets the layergroup of this map. + * @param {ol.layer.Group} layerGroup A layer group containing the layers in + * this map. + * @observable + * @api stable + */ +ol.Map.prototype.setLayerGroup = function(layerGroup) { + this.set(ol.MapProperty.LAYERGROUP, layerGroup); +}; + + +/** + * Set the size of this map. + * @param {ol.Size|undefined} size The size in pixels of the map in the DOM. + * @observable + * @api + */ +ol.Map.prototype.setSize = function(size) { + this.set(ol.MapProperty.SIZE, size); +}; + + +/** + * Set the target element to render this map into. + * @param {Element|string|undefined} target The Element or id of the Element + * that the map is rendered in. + * @observable + * @api stable + */ +ol.Map.prototype.setTarget = function(target) { + this.set(ol.MapProperty.TARGET, target); +}; + + +/** + * Set the view for this map. + * @param {ol.View} view The view that controls this map. + * @observable + * @api stable + */ +ol.Map.prototype.setView = function(view) { + this.set(ol.MapProperty.VIEW, view); +}; + + +/** + * @param {ol.Feature} feature Feature. + */ +ol.Map.prototype.skipFeature = function(feature) { + var featureUid = goog.getUid(feature).toString(); + this.skippedFeatureUids_[featureUid] = true; + this.render(); +}; + + +/** + * Force a recalculation of the map viewport size. This should be called when + * third-party code changes the size of the map viewport. + * @api stable + */ +ol.Map.prototype.updateSize = function() { + var targetElement = this.getTargetElement(); + + if (!targetElement) { + this.setSize(undefined); + } else { + var computedStyle = ol.global.getComputedStyle(targetElement); + this.setSize([ + targetElement.offsetWidth - + parseFloat(computedStyle['borderLeftWidth']) - + parseFloat(computedStyle['paddingLeft']) - + parseFloat(computedStyle['paddingRight']) - + parseFloat(computedStyle['borderRightWidth']), + targetElement.offsetHeight - + parseFloat(computedStyle['borderTopWidth']) - + parseFloat(computedStyle['paddingTop']) - + parseFloat(computedStyle['paddingBottom']) - + parseFloat(computedStyle['borderBottomWidth']) + ]); + } +}; + + +/** + * @param {ol.Feature} feature Feature. + */ +ol.Map.prototype.unskipFeature = function(feature) { + var featureUid = goog.getUid(feature).toString(); + delete this.skippedFeatureUids_[featureUid]; + this.render(); +}; + + +/** + * @param {olx.MapOptions} options Map options. + * @return {ol.MapOptionsInternal} Internal map options. + */ +ol.Map.createOptionsInternal = function(options) { + + /** + * @type {Element|Document} + */ + var keyboardEventTarget = null; + if (options.keyboardEventTarget !== undefined) { + keyboardEventTarget = typeof options.keyboardEventTarget === 'string' ? + document.getElementById(options.keyboardEventTarget) : + options.keyboardEventTarget; + } + + /** + * @type {Object.<string, *>} + */ + var values = {}; + + var logos = {}; + if (options.logo === undefined || + (typeof options.logo === 'boolean' && options.logo)) { + logos[ol.OL3_LOGO_URL] = ol.OL3_URL; + } else { + var logo = options.logo; + if (typeof logo === 'string') { + logos[logo] = ''; + } else if (logo instanceof HTMLElement) { + logos[goog.getUid(logo).toString()] = logo; + } else if (goog.isObject(logo)) { + goog.asserts.assertString(logo.href, 'logo.href should be a string'); + goog.asserts.assertString(logo.src, 'logo.src should be a string'); + logos[logo.src] = logo.href; + } + } + + var layerGroup = (options.layers instanceof ol.layer.Group) ? + options.layers : new ol.layer.Group({layers: options.layers}); + values[ol.MapProperty.LAYERGROUP] = layerGroup; + + values[ol.MapProperty.TARGET] = options.target; + + values[ol.MapProperty.VIEW] = options.view !== undefined ? + options.view : new ol.View(); + + /** + * @type {function(new: ol.renderer.Map, Element, ol.Map)} + */ + var rendererConstructor = ol.renderer.Map; + + /** + * @type {Array.<ol.RendererType>} + */ + var rendererTypes; + if (options.renderer !== undefined) { + if (Array.isArray(options.renderer)) { + rendererTypes = options.renderer; + } else if (typeof options.renderer === 'string') { + rendererTypes = [options.renderer]; + } else { + goog.asserts.fail('Incorrect format for renderer option'); + } + } else { + rendererTypes = ol.DEFAULT_RENDERER_TYPES; + } + + var i, ii; + for (i = 0, ii = rendererTypes.length; i < ii; ++i) { + /** @type {ol.RendererType} */ + var rendererType = rendererTypes[i]; + if (ol.ENABLE_CANVAS && rendererType == ol.RendererType.CANVAS) { + if (ol.has.CANVAS) { + rendererConstructor = ol.renderer.canvas.Map; + break; + } + } else if (ol.ENABLE_DOM && rendererType == ol.RendererType.DOM) { + if (ol.has.DOM) { + rendererConstructor = ol.renderer.dom.Map; + break; + } + } else if (ol.ENABLE_WEBGL && rendererType == ol.RendererType.WEBGL) { + if (ol.has.WEBGL) { + rendererConstructor = ol.renderer.webgl.Map; + break; + } + } + } + + var controls; + if (options.controls !== undefined) { + if (Array.isArray(options.controls)) { + controls = new ol.Collection(options.controls.slice()); + } else { + goog.asserts.assertInstanceof(options.controls, ol.Collection, + 'options.controls should be an ol.Collection'); + controls = options.controls; + } + } else { + controls = ol.control.defaults(); + } + + var interactions; + if (options.interactions !== undefined) { + if (Array.isArray(options.interactions)) { + interactions = new ol.Collection(options.interactions.slice()); + } else { + goog.asserts.assertInstanceof(options.interactions, ol.Collection, + 'options.interactions should be an ol.Collection'); + interactions = options.interactions; + } + } else { + interactions = ol.interaction.defaults(); + } + + var overlays; + if (options.overlays !== undefined) { + if (Array.isArray(options.overlays)) { + overlays = new ol.Collection(options.overlays.slice()); + } else { + goog.asserts.assertInstanceof(options.overlays, ol.Collection, + 'options.overlays should be an ol.Collection'); + overlays = options.overlays; + } + } else { + overlays = new ol.Collection(); + } + + return { + controls: controls, + interactions: interactions, + keyboardEventTarget: keyboardEventTarget, + logos: logos, + overlays: overlays, + rendererConstructor: rendererConstructor, + values: values + }; + +}; + + +ol.proj.common.add(); + +goog.provide('ol.Overlay'); +goog.provide('ol.OverlayPositioning'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.Map'); +goog.require('ol.MapEventType'); +goog.require('ol.Object'); +goog.require('ol.animation'); +goog.require('ol.dom'); +goog.require('ol.extent'); + + +/** + * @enum {string} + */ +ol.OverlayProperty = { + ELEMENT: 'element', + MAP: 'map', + OFFSET: 'offset', + POSITION: 'position', + POSITIONING: 'positioning' +}; + + +/** + * Overlay position: `'bottom-left'`, `'bottom-center'`, `'bottom-right'`, + * `'center-left'`, `'center-center'`, `'center-right'`, `'top-left'`, + * `'top-center'`, `'top-right'` + * @enum {string} + */ +ol.OverlayPositioning = { + BOTTOM_LEFT: 'bottom-left', + BOTTOM_CENTER: 'bottom-center', + BOTTOM_RIGHT: 'bottom-right', + CENTER_LEFT: 'center-left', + CENTER_CENTER: 'center-center', + CENTER_RIGHT: 'center-right', + TOP_LEFT: 'top-left', + TOP_CENTER: 'top-center', + TOP_RIGHT: 'top-right' +}; + + +/** + * @classdesc + * An element to be displayed over the map and attached to a single map + * location. Like {@link ol.control.Control}, Overlays are visible widgets. + * Unlike Controls, they are not in a fixed position on the screen, but are tied + * to a geographical coordinate, so panning the map will move an Overlay but not + * a Control. + * + * Example: + * + * var popup = new ol.Overlay({ + * element: document.getElementById('popup') + * }); + * popup.setPosition(coordinate); + * map.addOverlay(popup); + * + * @constructor + * @extends {ol.Object} + * @param {olx.OverlayOptions} options Overlay options. + * @api stable + */ +ol.Overlay = function(options) { + + ol.Object.call(this); + + /** + * @private + * @type {number|string|undefined} + */ + this.id_ = options.id; + + /** + * @private + * @type {boolean} + */ + this.insertFirst_ = options.insertFirst !== undefined ? + options.insertFirst : true; + + /** + * @private + * @type {boolean} + */ + this.stopEvent_ = options.stopEvent !== undefined ? options.stopEvent : true; + + /** + * @private + * @type {Element} + */ + this.element_ = document.createElement('DIV'); + this.element_.className = 'ol-overlay-container'; + this.element_.style.position = 'absolute'; + + /** + * @protected + * @type {boolean} + */ + this.autoPan = options.autoPan !== undefined ? options.autoPan : false; + + /** + * @private + * @type {olx.animation.PanOptions} + */ + this.autoPanAnimation_ = options.autoPanAnimation !== undefined ? + options.autoPanAnimation : /** @type {olx.animation.PanOptions} */ ({}); + + /** + * @private + * @type {number} + */ + this.autoPanMargin_ = options.autoPanMargin !== undefined ? + options.autoPanMargin : 20; + + /** + * @private + * @type {{bottom_: string, + * left_: string, + * right_: string, + * top_: string, + * visible: boolean}} + */ + this.rendered_ = { + bottom_: '', + left_: '', + right_: '', + top_: '', + visible: true + }; + + /** + * @private + * @type {?ol.EventsKey} + */ + this.mapPostrenderListenerKey_ = null; + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.OverlayProperty.ELEMENT), + this.handleElementChanged, this); + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.OverlayProperty.MAP), + this.handleMapChanged, this); + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.OverlayProperty.OFFSET), + this.handleOffsetChanged, this); + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.OverlayProperty.POSITION), + this.handlePositionChanged, this); + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.OverlayProperty.POSITIONING), + this.handlePositioningChanged, this); + + if (options.element !== undefined) { + this.setElement(options.element); + } + + this.setOffset(options.offset !== undefined ? options.offset : [0, 0]); + + this.setPositioning(options.positioning !== undefined ? + /** @type {ol.OverlayPositioning} */ (options.positioning) : + ol.OverlayPositioning.TOP_LEFT); + + if (options.position !== undefined) { + this.setPosition(options.position); + } + +}; +ol.inherits(ol.Overlay, ol.Object); + + +/** + * Get the DOM element of this overlay. + * @return {Element|undefined} The Element containing the overlay. + * @observable + * @api stable + */ +ol.Overlay.prototype.getElement = function() { + return /** @type {Element|undefined} */ ( + this.get(ol.OverlayProperty.ELEMENT)); +}; + + +/** + * Get the overlay identifier which is set on constructor. + * @return {number|string|undefined} Id. + * @api + */ +ol.Overlay.prototype.getId = function() { + return this.id_; +}; + + +/** + * Get the map associated with this overlay. + * @return {ol.Map|undefined} The map that the overlay is part of. + * @observable + * @api stable + */ +ol.Overlay.prototype.getMap = function() { + return /** @type {ol.Map|undefined} */ ( + this.get(ol.OverlayProperty.MAP)); +}; + + +/** + * Get the offset of this overlay. + * @return {Array.<number>} The offset. + * @observable + * @api stable + */ +ol.Overlay.prototype.getOffset = function() { + return /** @type {Array.<number>} */ ( + this.get(ol.OverlayProperty.OFFSET)); +}; + + +/** + * Get the current position of this overlay. + * @return {ol.Coordinate|undefined} The spatial point that the overlay is + * anchored at. + * @observable + * @api stable + */ +ol.Overlay.prototype.getPosition = function() { + return /** @type {ol.Coordinate|undefined} */ ( + this.get(ol.OverlayProperty.POSITION)); +}; + + +/** + * Get the current positioning of this overlay. + * @return {ol.OverlayPositioning} How the overlay is positioned + * relative to its point on the map. + * @observable + * @api stable + */ +ol.Overlay.prototype.getPositioning = function() { + return /** @type {ol.OverlayPositioning} */ ( + this.get(ol.OverlayProperty.POSITIONING)); +}; + + +/** + * @protected + */ +ol.Overlay.prototype.handleElementChanged = function() { + ol.dom.removeChildren(this.element_); + var element = this.getElement(); + if (element) { + this.element_.appendChild(element); + } +}; + + +/** + * @protected + */ +ol.Overlay.prototype.handleMapChanged = function() { + if (this.mapPostrenderListenerKey_) { + ol.dom.removeNode(this.element_); + ol.events.unlistenByKey(this.mapPostrenderListenerKey_); + this.mapPostrenderListenerKey_ = null; + } + var map = this.getMap(); + if (map) { + this.mapPostrenderListenerKey_ = ol.events.listen(map, + ol.MapEventType.POSTRENDER, this.render, this); + this.updatePixelPosition(); + var container = this.stopEvent_ ? + map.getOverlayContainerStopEvent() : map.getOverlayContainer(); + if (this.insertFirst_) { + container.insertBefore(this.element_, container.childNodes[0] || null); + } else { + container.appendChild(this.element_); + } + } +}; + + +/** + * @protected + */ +ol.Overlay.prototype.render = function() { + this.updatePixelPosition(); +}; + + +/** + * @protected + */ +ol.Overlay.prototype.handleOffsetChanged = function() { + this.updatePixelPosition(); +}; + + +/** + * @protected + */ +ol.Overlay.prototype.handlePositionChanged = function() { + this.updatePixelPosition(); + if (this.get(ol.OverlayProperty.POSITION) !== undefined && this.autoPan) { + this.panIntoView_(); + } +}; + + +/** + * @protected + */ +ol.Overlay.prototype.handlePositioningChanged = function() { + this.updatePixelPosition(); +}; + + +/** + * Set the DOM element to be associated with this overlay. + * @param {Element|undefined} element The Element containing the overlay. + * @observable + * @api stable + */ +ol.Overlay.prototype.setElement = function(element) { + this.set(ol.OverlayProperty.ELEMENT, element); +}; + + +/** + * Set the map to be associated with this overlay. + * @param {ol.Map|undefined} map The map that the overlay is part of. + * @observable + * @api stable + */ +ol.Overlay.prototype.setMap = function(map) { + this.set(ol.OverlayProperty.MAP, map); +}; + + +/** + * Set the offset for this overlay. + * @param {Array.<number>} offset Offset. + * @observable + * @api stable + */ +ol.Overlay.prototype.setOffset = function(offset) { + this.set(ol.OverlayProperty.OFFSET, offset); +}; + + +/** + * Set the position for this overlay. If the position is `undefined` the + * overlay is hidden. + * @param {ol.Coordinate|undefined} position The spatial point that the overlay + * is anchored at. + * @observable + * @api stable + */ +ol.Overlay.prototype.setPosition = function(position) { + this.set(ol.OverlayProperty.POSITION, position); +}; + + +/** + * Pan the map so that the overlay is entirely visible in the current viewport + * (if necessary). + * @private + */ +ol.Overlay.prototype.panIntoView_ = function() { + goog.asserts.assert(this.autoPan, 'this.autoPan should be true'); + var map = this.getMap(); + + if (map === undefined || !map.getTargetElement()) { + return; + } + + var mapRect = this.getRect_(map.getTargetElement(), map.getSize()); + var element = this.getElement(); + goog.asserts.assert(element, 'element should be defined'); + var overlayRect = this.getRect_(element, + [ol.dom.outerWidth(element), ol.dom.outerHeight(element)]); + + var margin = this.autoPanMargin_; + if (!ol.extent.containsExtent(mapRect, overlayRect)) { + // the overlay is not completely inside the viewport, so pan the map + var offsetLeft = overlayRect[0] - mapRect[0]; + var offsetRight = mapRect[2] - overlayRect[2]; + var offsetTop = overlayRect[1] - mapRect[1]; + var offsetBottom = mapRect[3] - overlayRect[3]; + + var delta = [0, 0]; + if (offsetLeft < 0) { + // move map to the left + delta[0] = offsetLeft - margin; + } else if (offsetRight < 0) { + // move map to the right + delta[0] = Math.abs(offsetRight) + margin; + } + if (offsetTop < 0) { + // move map up + delta[1] = offsetTop - margin; + } else if (offsetBottom < 0) { + // move map down + delta[1] = Math.abs(offsetBottom) + margin; + } + + if (delta[0] !== 0 || delta[1] !== 0) { + var center = map.getView().getCenter(); + goog.asserts.assert(center !== undefined, 'center should be defined'); + var centerPx = map.getPixelFromCoordinate(center); + var newCenterPx = [ + centerPx[0] + delta[0], + centerPx[1] + delta[1] + ]; + + if (this.autoPanAnimation_) { + this.autoPanAnimation_.source = center; + map.beforeRender(ol.animation.pan(this.autoPanAnimation_)); + } + map.getView().setCenter(map.getCoordinateFromPixel(newCenterPx)); + } + } +}; + + +/** + * Get the extent of an element relative to the document + * @param {Element|undefined} element The element. + * @param {ol.Size|undefined} size The size of the element. + * @return {ol.Extent} The extent. + * @private + */ +ol.Overlay.prototype.getRect_ = function(element, size) { + goog.asserts.assert(element, 'element should be defined'); + goog.asserts.assert(size !== undefined, 'size should be defined'); + + var box = element.getBoundingClientRect(); + var offsetX = box.left + ol.global.pageXOffset; + var offsetY = box.top + ol.global.pageYOffset; + return [ + offsetX, + offsetY, + offsetX + size[0], + offsetY + size[1] + ]; +}; + + +/** + * Set the positioning for this overlay. + * @param {ol.OverlayPositioning} positioning how the overlay is + * positioned relative to its point on the map. + * @observable + * @api stable + */ +ol.Overlay.prototype.setPositioning = function(positioning) { + this.set(ol.OverlayProperty.POSITIONING, positioning); +}; + + +/** + * Modify the visibility of the element. + * @param {boolean} visible Element visibility. + * @protected + */ +ol.Overlay.prototype.setVisible = function(visible) { + if (this.rendered_.visible !== visible) { + this.element_.style.display = visible ? '' : 'none'; + this.rendered_.visible = visible; + } +}; + + +/** + * Update pixel position. + * @protected + */ +ol.Overlay.prototype.updatePixelPosition = function() { + var map = this.getMap(); + var position = this.getPosition(); + if (map === undefined || !map.isRendered() || position === undefined) { + this.setVisible(false); + return; + } + + var pixel = map.getPixelFromCoordinate(position); + var mapSize = map.getSize(); + this.updateRenderedPosition(pixel, mapSize); +}; + + +/** + * @param {ol.Pixel} pixel The pixel location. + * @param {ol.Size|undefined} mapSize The map size. + * @protected + */ +ol.Overlay.prototype.updateRenderedPosition = function(pixel, mapSize) { + goog.asserts.assert(pixel, 'pixel should not be null'); + goog.asserts.assert(mapSize !== undefined, 'mapSize should be defined'); + var style = this.element_.style; + var offset = this.getOffset(); + goog.asserts.assert(Array.isArray(offset), 'offset should be an array'); + + var positioning = this.getPositioning(); + goog.asserts.assert(positioning !== undefined, + 'positioning should be defined'); + + var offsetX = offset[0]; + var offsetY = offset[1]; + if (positioning == ol.OverlayPositioning.BOTTOM_RIGHT || + positioning == ol.OverlayPositioning.CENTER_RIGHT || + positioning == ol.OverlayPositioning.TOP_RIGHT) { + if (this.rendered_.left_ !== '') { + this.rendered_.left_ = style.left = ''; + } + var right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px'; + if (this.rendered_.right_ != right) { + this.rendered_.right_ = style.right = right; + } + } else { + if (this.rendered_.right_ !== '') { + this.rendered_.right_ = style.right = ''; + } + if (positioning == ol.OverlayPositioning.BOTTOM_CENTER || + positioning == ol.OverlayPositioning.CENTER_CENTER || + positioning == ol.OverlayPositioning.TOP_CENTER) { + offsetX -= this.element_.offsetWidth / 2; + } + var left = Math.round(pixel[0] + offsetX) + 'px'; + if (this.rendered_.left_ != left) { + this.rendered_.left_ = style.left = left; + } + } + if (positioning == ol.OverlayPositioning.BOTTOM_LEFT || + positioning == ol.OverlayPositioning.BOTTOM_CENTER || + positioning == ol.OverlayPositioning.BOTTOM_RIGHT) { + if (this.rendered_.top_ !== '') { + this.rendered_.top_ = style.top = ''; + } + var bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px'; + if (this.rendered_.bottom_ != bottom) { + this.rendered_.bottom_ = style.bottom = bottom; + } + } else { + if (this.rendered_.bottom_ !== '') { + this.rendered_.bottom_ = style.bottom = ''; + } + if (positioning == ol.OverlayPositioning.CENTER_LEFT || + positioning == ol.OverlayPositioning.CENTER_CENTER || + positioning == ol.OverlayPositioning.CENTER_RIGHT) { + offsetY -= this.element_.offsetHeight / 2; + } + var top = Math.round(pixel[1] + offsetY) + 'px'; + if (this.rendered_.top_ != top) { + this.rendered_.top_ = style.top = top; + } + } + + this.setVisible(true); +}; + +goog.provide('ol.control.OverviewMap'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol'); +goog.require('ol.Collection'); +goog.require('ol.Map'); +goog.require('ol.MapEventType'); +goog.require('ol.Object'); +goog.require('ol.ObjectEventType'); +goog.require('ol.Overlay'); +goog.require('ol.OverlayPositioning'); +goog.require('ol.View'); +goog.require('ol.ViewProperty'); +goog.require('ol.control.Control'); +goog.require('ol.coordinate'); +goog.require('ol.css'); +goog.require('ol.dom'); +goog.require('ol.extent'); + + +/** + * Create a new control with a map acting as an overview map for an other + * defined map. + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.OverviewMapOptions=} opt_options OverviewMap options. + * @api + */ +ol.control.OverviewMap = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + /** + * @type {boolean} + * @private + */ + this.collapsed_ = options.collapsed !== undefined ? options.collapsed : true; + + /** + * @private + * @type {boolean} + */ + this.collapsible_ = options.collapsible !== undefined ? + options.collapsible : true; + + if (!this.collapsible_) { + this.collapsed_ = false; + } + + var className = options.className !== undefined ? options.className : 'ol-overviewmap'; + + var tipLabel = options.tipLabel !== undefined ? options.tipLabel : 'Overview map'; + + var collapseLabel = options.collapseLabel !== undefined ? options.collapseLabel : '\u00AB'; + + if (typeof collapseLabel === 'string') { + /** + * @private + * @type {Node} + */ + this.collapseLabel_ = document.createElement('span'); + this.collapseLabel_.textContent = collapseLabel; + } else { + this.collapseLabel_ = collapseLabel; + } + + var label = options.label !== undefined ? options.label : '\u00BB'; + + + if (typeof label === 'string') { + /** + * @private + * @type {Node} + */ + this.label_ = document.createElement('span'); + this.label_.textContent = label; + } else { + this.label_ = label; + } + + var activeLabel = (this.collapsible_ && !this.collapsed_) ? + this.collapseLabel_ : this.label_; + var button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild(activeLabel); + + ol.events.listen(button, ol.events.EventType.CLICK, + this.handleClick_, this); + + var ovmapDiv = document.createElement('DIV'); + ovmapDiv.className = 'ol-overviewmap-map'; + + /** + * @type {ol.Map} + * @private + */ + this.ovmap_ = new ol.Map({ + controls: new ol.Collection(), + interactions: new ol.Collection(), + target: ovmapDiv, + view: options.view + }); + var ovmap = this.ovmap_; + + if (options.layers) { + options.layers.forEach( + /** + * @param {ol.layer.Layer} layer Layer. + */ + function(layer) { + ovmap.addLayer(layer); + }, this); + } + + var box = document.createElement('DIV'); + box.className = 'ol-overviewmap-box'; + box.style.boxSizing = 'border-box'; + + /** + * @type {ol.Overlay} + * @private + */ + this.boxOverlay_ = new ol.Overlay({ + position: [0, 0], + positioning: ol.OverlayPositioning.BOTTOM_LEFT, + element: box + }); + this.ovmap_.addOverlay(this.boxOverlay_); + + var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + + ol.css.CLASS_CONTROL + + (this.collapsed_ && this.collapsible_ ? ' ol-collapsed' : '') + + (this.collapsible_ ? '' : ' ol-uncollapsible'); + var element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(ovmapDiv); + element.appendChild(button); + + var render = options.render ? options.render : ol.control.OverviewMap.render; + + ol.control.Control.call(this, { + element: element, + render: render, + target: options.target + }); +}; +ol.inherits(ol.control.OverviewMap, ol.control.Control); + + +/** + * @inheritDoc + * @api + */ +ol.control.OverviewMap.prototype.setMap = function(map) { + var oldMap = this.getMap(); + if (map === oldMap) { + return; + } + if (oldMap) { + var oldView = oldMap.getView(); + if (oldView) { + this.unbindView_(oldView); + } + } + ol.control.Control.prototype.setMap.call(this, map); + + if (map) { + this.listenerKeys.push(ol.events.listen( + map, ol.ObjectEventType.PROPERTYCHANGE, + this.handleMapPropertyChange_, this)); + + // TODO: to really support map switching, this would need to be reworked + if (this.ovmap_.getLayers().getLength() === 0) { + this.ovmap_.setLayerGroup(map.getLayerGroup()); + } + + var view = map.getView(); + if (view) { + this.bindView_(view); + if (view.isDef()) { + this.ovmap_.updateSize(); + this.resetExtent_(); + } + } + } +}; + + +/** + * Handle map property changes. This only deals with changes to the map's view. + * @param {ol.ObjectEvent} event The propertychange event. + * @private + */ +ol.control.OverviewMap.prototype.handleMapPropertyChange_ = function(event) { + if (event.key === ol.MapProperty.VIEW) { + var oldView = /** @type {ol.View} */ (event.oldValue); + if (oldView) { + this.unbindView_(oldView); + } + var newView = this.getMap().getView(); + this.bindView_(newView); + } +}; + + +/** + * Register listeners for view property changes. + * @param {ol.View} view The view. + * @private + */ +ol.control.OverviewMap.prototype.bindView_ = function(view) { + ol.events.listen(view, + ol.Object.getChangeEventType(ol.ViewProperty.ROTATION), + this.handleRotationChanged_, this); +}; + + +/** + * Unregister listeners for view property changes. + * @param {ol.View} view The view. + * @private + */ +ol.control.OverviewMap.prototype.unbindView_ = function(view) { + ol.events.unlisten(view, + ol.Object.getChangeEventType(ol.ViewProperty.ROTATION), + this.handleRotationChanged_, this); +}; + + +/** + * Handle rotation changes to the main map. + * TODO: This should rotate the extent rectrangle instead of the + * overview map's view. + * @private + */ +ol.control.OverviewMap.prototype.handleRotationChanged_ = function() { + this.ovmap_.getView().setRotation(this.getMap().getView().getRotation()); +}; + + +/** + * Update the overview map element. + * @param {ol.MapEvent} mapEvent Map event. + * @this {ol.control.OverviewMap} + * @api + */ +ol.control.OverviewMap.render = function(mapEvent) { + this.validateExtent_(); + this.updateBox_(); +}; + + +/** + * Reset the overview map extent if the box size (width or + * height) is less than the size of the overview map size times minRatio + * or is greater than the size of the overview size times maxRatio. + * + * If the map extent was not reset, the box size can fits in the defined + * ratio sizes. This method then checks if is contained inside the overview + * map current extent. If not, recenter the overview map to the current + * main map center location. + * @private + */ +ol.control.OverviewMap.prototype.validateExtent_ = function() { + var map = this.getMap(); + var ovmap = this.ovmap_; + + if (!map.isRendered() || !ovmap.isRendered()) { + return; + } + + var mapSize = map.getSize(); + goog.asserts.assertArray(mapSize, 'mapSize should be an array'); + + var view = map.getView(); + goog.asserts.assert(view, 'view should be defined'); + var extent = view.calculateExtent(mapSize); + + var ovmapSize = ovmap.getSize(); + goog.asserts.assertArray(ovmapSize, 'ovmapSize should be an array'); + + var ovview = ovmap.getView(); + goog.asserts.assert(ovview, 'ovview should be defined'); + var ovextent = ovview.calculateExtent(ovmapSize); + + var topLeftPixel = + ovmap.getPixelFromCoordinate(ol.extent.getTopLeft(extent)); + var bottomRightPixel = + ovmap.getPixelFromCoordinate(ol.extent.getBottomRight(extent)); + + var boxWidth = Math.abs(topLeftPixel[0] - bottomRightPixel[0]); + var boxHeight = Math.abs(topLeftPixel[1] - bottomRightPixel[1]); + + var ovmapWidth = ovmapSize[0]; + var ovmapHeight = ovmapSize[1]; + + if (boxWidth < ovmapWidth * ol.OVERVIEWMAP_MIN_RATIO || + boxHeight < ovmapHeight * ol.OVERVIEWMAP_MIN_RATIO || + boxWidth > ovmapWidth * ol.OVERVIEWMAP_MAX_RATIO || + boxHeight > ovmapHeight * ol.OVERVIEWMAP_MAX_RATIO) { + this.resetExtent_(); + } else if (!ol.extent.containsExtent(ovextent, extent)) { + this.recenter_(); + } +}; + + +/** + * Reset the overview map extent to half calculated min and max ratio times + * the extent of the main map. + * @private + */ +ol.control.OverviewMap.prototype.resetExtent_ = function() { + if (ol.OVERVIEWMAP_MAX_RATIO === 0 || ol.OVERVIEWMAP_MIN_RATIO === 0) { + return; + } + + var map = this.getMap(); + var ovmap = this.ovmap_; + + var mapSize = map.getSize(); + goog.asserts.assertArray(mapSize, 'mapSize should be an array'); + + var view = map.getView(); + goog.asserts.assert(view, 'view should be defined'); + var extent = view.calculateExtent(mapSize); + + var ovmapSize = ovmap.getSize(); + goog.asserts.assertArray(ovmapSize, 'ovmapSize should be an array'); + + var ovview = ovmap.getView(); + goog.asserts.assert(ovview, 'ovview should be defined'); + + // get how many times the current map overview could hold different + // box sizes using the min and max ratio, pick the step in the middle used + // to calculate the extent from the main map to set it to the overview map, + var steps = Math.log( + ol.OVERVIEWMAP_MAX_RATIO / ol.OVERVIEWMAP_MIN_RATIO) / Math.LN2; + var ratio = 1 / (Math.pow(2, steps / 2) * ol.OVERVIEWMAP_MIN_RATIO); + ol.extent.scaleFromCenter(extent, ratio); + ovview.fit(extent, ovmapSize); +}; + + +/** + * Set the center of the overview map to the map center without changing its + * resolution. + * @private + */ +ol.control.OverviewMap.prototype.recenter_ = function() { + var map = this.getMap(); + var ovmap = this.ovmap_; + + var view = map.getView(); + goog.asserts.assert(view, 'view should be defined'); + + var ovview = ovmap.getView(); + goog.asserts.assert(ovview, 'ovview should be defined'); + + ovview.setCenter(view.getCenter()); +}; + + +/** + * Update the box using the main map extent + * @private + */ +ol.control.OverviewMap.prototype.updateBox_ = function() { + var map = this.getMap(); + var ovmap = this.ovmap_; + + if (!map.isRendered() || !ovmap.isRendered()) { + return; + } + + var mapSize = map.getSize(); + goog.asserts.assertArray(mapSize, 'mapSize should be an array'); + + var view = map.getView(); + goog.asserts.assert(view, 'view should be defined'); + + var ovview = ovmap.getView(); + goog.asserts.assert(ovview, 'ovview should be defined'); + + var ovmapSize = ovmap.getSize(); + goog.asserts.assertArray(ovmapSize, 'ovmapSize should be an array'); + + var rotation = view.getRotation(); + goog.asserts.assert(rotation !== undefined, 'rotation should be defined'); + + var overlay = this.boxOverlay_; + var box = this.boxOverlay_.getElement(); + var extent = view.calculateExtent(mapSize); + var ovresolution = ovview.getResolution(); + var bottomLeft = ol.extent.getBottomLeft(extent); + var topRight = ol.extent.getTopRight(extent); + + // set position using bottom left coordinates + var rotateBottomLeft = this.calculateCoordinateRotate_(rotation, bottomLeft); + overlay.setPosition(rotateBottomLeft); + + // set box size calculated from map extent size and overview map resolution + if (box) { + box.style.width = Math.abs((bottomLeft[0] - topRight[0]) / ovresolution) + 'px'; + box.style.height = Math.abs((topRight[1] - bottomLeft[1]) / ovresolution) + 'px'; + } +}; + + +/** + * @param {number} rotation Target rotation. + * @param {ol.Coordinate} coordinate Coordinate. + * @return {ol.Coordinate|undefined} Coordinate for rotation and center anchor. + * @private + */ +ol.control.OverviewMap.prototype.calculateCoordinateRotate_ = function( + rotation, coordinate) { + var coordinateRotate; + + var map = this.getMap(); + var view = map.getView(); + goog.asserts.assert(view, 'view should be defined'); + + var currentCenter = view.getCenter(); + + if (currentCenter) { + coordinateRotate = [ + coordinate[0] - currentCenter[0], + coordinate[1] - currentCenter[1] + ]; + ol.coordinate.rotate(coordinateRotate, rotation); + ol.coordinate.add(coordinateRotate, currentCenter); + } + return coordinateRotate; +}; + + +/** + * @param {Event} event The event to handle + * @private + */ +ol.control.OverviewMap.prototype.handleClick_ = function(event) { + event.preventDefault(); + this.handleToggle_(); +}; + + +/** + * @private + */ +ol.control.OverviewMap.prototype.handleToggle_ = function() { + this.element.classList.toggle('ol-collapsed'); + if (this.collapsed_) { + ol.dom.replaceNode(this.collapseLabel_, this.label_); + } else { + ol.dom.replaceNode(this.label_, this.collapseLabel_); + } + this.collapsed_ = !this.collapsed_; + + // manage overview map if it had not been rendered before and control + // is expanded + var ovmap = this.ovmap_; + if (!this.collapsed_ && !ovmap.isRendered()) { + ovmap.updateSize(); + this.resetExtent_(); + ol.events.listenOnce(ovmap, ol.MapEventType.POSTRENDER, + function(event) { + this.updateBox_(); + }, + this); + } +}; + + +/** + * Return `true` if the overview map is collapsible, `false` otherwise. + * @return {boolean} True if the widget is collapsible. + * @api stable + */ +ol.control.OverviewMap.prototype.getCollapsible = function() { + return this.collapsible_; +}; + + +/** + * Set whether the overview map should be collapsible. + * @param {boolean} collapsible True if the widget is collapsible. + * @api stable + */ +ol.control.OverviewMap.prototype.setCollapsible = function(collapsible) { + if (this.collapsible_ === collapsible) { + return; + } + this.collapsible_ = collapsible; + this.element.classList.toggle('ol-uncollapsible'); + if (!collapsible && this.collapsed_) { + this.handleToggle_(); + } +}; + + +/** + * Collapse or expand the overview map according to the passed parameter. Will + * not do anything if the overview map isn't collapsible or if the current + * collapsed state is already the one requested. + * @param {boolean} collapsed True if the widget is collapsed. + * @api stable + */ +ol.control.OverviewMap.prototype.setCollapsed = function(collapsed) { + if (!this.collapsible_ || this.collapsed_ === collapsed) { + return; + } + this.handleToggle_(); +}; + + +/** + * Determine if the overview map is collapsed. + * @return {boolean} The overview map is collapsed. + * @api stable + */ +ol.control.OverviewMap.prototype.getCollapsed = function() { + return this.collapsed_; +}; + + +/** + * Return the overview map. + * @return {ol.Map} Overview map. + * @api + */ +ol.control.OverviewMap.prototype.getOverviewMap = function() { + return this.ovmap_; +}; + +goog.provide('ol.control.ScaleLine'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol'); +goog.require('ol.Object'); +goog.require('ol.control.Control'); +goog.require('ol.css'); +goog.require('ol.proj.METERS_PER_UNIT'); +goog.require('ol.proj.Units'); + + +/** + * @enum {string} + */ +ol.control.ScaleLineProperty = { + UNITS: 'units' +}; + + +/** + * Units for the scale line. Supported values are `'degrees'`, `'imperial'`, + * `'nautical'`, `'metric'`, `'us'`. + * @enum {string} + */ +ol.control.ScaleLineUnits = { + DEGREES: 'degrees', + IMPERIAL: 'imperial', + NAUTICAL: 'nautical', + METRIC: 'metric', + US: 'us' +}; + + +/** + * @classdesc + * A control displaying rough x-axis distances, calculated for the center of the + * viewport. + * No scale line will be shown when the x-axis distance cannot be calculated in + * the view projection (e.g. at or beyond the poles in EPSG:4326). + * By default the scale line will show in the bottom left portion of the map, + * but this can be changed by using the css selector `.ol-scale-line`. + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.ScaleLineOptions=} opt_options Scale line options. + * @api stable + */ +ol.control.ScaleLine = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + var className = options.className !== undefined ? options.className : 'ol-scale-line'; + + /** + * @private + * @type {Element} + */ + this.innerElement_ = document.createElement('DIV'); + this.innerElement_.className = className + '-inner'; + + /** + * @private + * @type {Element} + */ + this.element_ = document.createElement('DIV'); + this.element_.className = className + ' ' + ol.css.CLASS_UNSELECTABLE; + this.element_.appendChild(this.innerElement_); + + /** + * @private + * @type {?olx.ViewState} + */ + this.viewState_ = null; + + /** + * @private + * @type {number} + */ + this.minWidth_ = options.minWidth !== undefined ? options.minWidth : 64; + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = false; + + /** + * @private + * @type {number|undefined} + */ + this.renderedWidth_ = undefined; + + /** + * @private + * @type {string} + */ + this.renderedHTML_ = ''; + + var render = options.render ? options.render : ol.control.ScaleLine.render; + + ol.control.Control.call(this, { + element: this.element_, + render: render, + target: options.target + }); + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.control.ScaleLineProperty.UNITS), + this.handleUnitsChanged_, this); + + this.setUnits(/** @type {ol.control.ScaleLineUnits} */ (options.units) || + ol.control.ScaleLineUnits.METRIC); + +}; +ol.inherits(ol.control.ScaleLine, ol.control.Control); + + +/** + * @const + * @type {Array.<number>} + */ +ol.control.ScaleLine.LEADING_DIGITS = [1, 2, 5]; + + +/** + * Return the units to use in the scale line. + * @return {ol.control.ScaleLineUnits|undefined} The units to use in the scale + * line. + * @observable + * @api stable + */ +ol.control.ScaleLine.prototype.getUnits = function() { + return /** @type {ol.control.ScaleLineUnits|undefined} */ ( + this.get(ol.control.ScaleLineProperty.UNITS)); +}; + + +/** + * Update the scale line element. + * @param {ol.MapEvent} mapEvent Map event. + * @this {ol.control.ScaleLine} + * @api + */ +ol.control.ScaleLine.render = function(mapEvent) { + var frameState = mapEvent.frameState; + if (!frameState) { + this.viewState_ = null; + } else { + this.viewState_ = frameState.viewState; + } + this.updateElement_(); +}; + + +/** + * @private + */ +ol.control.ScaleLine.prototype.handleUnitsChanged_ = function() { + this.updateElement_(); +}; + + +/** + * Set the units to use in the scale line. + * @param {ol.control.ScaleLineUnits} units The units to use in the scale line. + * @observable + * @api stable + */ +ol.control.ScaleLine.prototype.setUnits = function(units) { + this.set(ol.control.ScaleLineProperty.UNITS, units); +}; + + +/** + * @private + */ +ol.control.ScaleLine.prototype.updateElement_ = function() { + var viewState = this.viewState_; + + if (!viewState) { + if (this.renderedVisible_) { + this.element_.style.display = 'none'; + this.renderedVisible_ = false; + } + return; + } + + var center = viewState.center; + var projection = viewState.projection; + var metersPerUnit = projection.getMetersPerUnit(); + var pointResolution = + projection.getPointResolution(viewState.resolution, center) * + metersPerUnit; + + var nominalCount = this.minWidth_ * pointResolution; + var suffix = ''; + var units = this.getUnits(); + if (units == ol.control.ScaleLineUnits.DEGREES) { + var metersPerDegree = ol.proj.METERS_PER_UNIT[ol.proj.Units.DEGREES]; + pointResolution /= metersPerDegree; + if (nominalCount < metersPerDegree / 60) { + suffix = '\u2033'; // seconds + pointResolution *= 3600; + } else if (nominalCount < metersPerDegree) { + suffix = '\u2032'; // minutes + pointResolution *= 60; + } else { + suffix = '\u00b0'; // degrees + } + } else if (units == ol.control.ScaleLineUnits.IMPERIAL) { + if (nominalCount < 0.9144) { + suffix = 'in'; + pointResolution /= 0.0254; + } else if (nominalCount < 1609.344) { + suffix = 'ft'; + pointResolution /= 0.3048; + } else { + suffix = 'mi'; + pointResolution /= 1609.344; + } + } else if (units == ol.control.ScaleLineUnits.NAUTICAL) { + pointResolution /= 1852; + suffix = 'nm'; + } else if (units == ol.control.ScaleLineUnits.METRIC) { + if (nominalCount < 1) { + suffix = 'mm'; + pointResolution *= 1000; + } else if (nominalCount < 1000) { + suffix = 'm'; + } else { + suffix = 'km'; + pointResolution /= 1000; + } + } else if (units == ol.control.ScaleLineUnits.US) { + if (nominalCount < 0.9144) { + suffix = 'in'; + pointResolution *= 39.37; + } else if (nominalCount < 1609.344) { + suffix = 'ft'; + pointResolution /= 0.30480061; + } else { + suffix = 'mi'; + pointResolution /= 1609.3472; + } + } else { + goog.asserts.fail('Scale line element cannot be updated'); + } + + var i = 3 * Math.floor( + Math.log(this.minWidth_ * pointResolution) / Math.log(10)); + var count, width; + while (true) { + count = ol.control.ScaleLine.LEADING_DIGITS[((i % 3) + 3) % 3] * + Math.pow(10, Math.floor(i / 3)); + width = Math.round(count / pointResolution); + if (isNaN(width)) { + this.element_.style.display = 'none'; + this.renderedVisible_ = false; + return; + } else if (width >= this.minWidth_) { + break; + } + ++i; + } + + var html = count + ' ' + suffix; + if (this.renderedHTML_ != html) { + this.innerElement_.innerHTML = html; + this.renderedHTML_ = html; + } + + if (this.renderedWidth_ != width) { + this.innerElement_.style.width = width + 'px'; + this.renderedWidth_ = width; + } + + if (!this.renderedVisible_) { + this.element_.style.display = ''; + this.renderedVisible_ = true; + } + +}; + +// FIXME should possibly show tooltip when dragging? + +goog.provide('ol.control.ZoomSlider'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.EventType'); +goog.require('ol.pointer.PointerEventHandler'); +goog.require('ol.ViewHint'); +goog.require('ol.animation'); +goog.require('ol.control.Control'); +goog.require('ol.css'); +goog.require('ol.easing'); +goog.require('ol.math'); + + +/** + * @classdesc + * A slider type of control for zooming. + * + * Example: + * + * map.addControl(new ol.control.ZoomSlider()); + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.ZoomSliderOptions=} opt_options Zoom slider options. + * @api stable + */ +ol.control.ZoomSlider = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + /** + * Will hold the current resolution of the view. + * + * @type {number|undefined} + * @private + */ + this.currentResolution_ = undefined; + + /** + * The direction of the slider. Will be determined from actual display of the + * container and defaults to ol.control.ZoomSlider.direction.VERTICAL. + * + * @type {ol.control.ZoomSlider.direction} + * @private + */ + this.direction_ = ol.control.ZoomSlider.direction.VERTICAL; + + /** + * @type {boolean} + * @private + */ + this.dragging_; + + /** + * @type {!Array.<ol.EventsKey>} + * @private + */ + this.dragListenerKeys_ = []; + + /** + * @type {number} + * @private + */ + this.heightLimit_ = 0; + + /** + * @type {number} + * @private + */ + this.widthLimit_ = 0; + + /** + * @type {number|undefined} + * @private + */ + this.previousX_; + + /** + * @type {number|undefined} + * @private + */ + this.previousY_; + + /** + * The calculated thumb size (border box plus margins). Set when initSlider_ + * is called. + * @type {ol.Size} + * @private + */ + this.thumbSize_ = null; + + /** + * Whether the slider is initialized. + * @type {boolean} + * @private + */ + this.sliderInitialized_ = false; + + /** + * @type {number} + * @private + */ + this.duration_ = options.duration !== undefined ? options.duration : 200; + + var className = options.className !== undefined ? options.className : 'ol-zoomslider'; + var thumbElement = document.createElement('button'); + thumbElement.setAttribute('type', 'button'); + thumbElement.className = className + '-thumb ' + ol.css.CLASS_UNSELECTABLE; + var containerElement = document.createElement('div'); + containerElement.className = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + ol.css.CLASS_CONTROL; + containerElement.appendChild(thumbElement); + /** + * @type {ol.pointer.PointerEventHandler} + * @private + */ + this.dragger_ = new ol.pointer.PointerEventHandler(containerElement); + + ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERDOWN, + this.handleDraggerStart_, this); + ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERMOVE, + this.handleDraggerDrag_, this); + ol.events.listen(this.dragger_, ol.pointer.EventType.POINTERUP, + this.handleDraggerEnd_, this); + + ol.events.listen(containerElement, ol.events.EventType.CLICK, + this.handleContainerClick_, this); + ol.events.listen(thumbElement, ol.events.EventType.CLICK, + ol.events.Event.stopPropagation); + + var render = options.render ? options.render : ol.control.ZoomSlider.render; + + ol.control.Control.call(this, { + element: containerElement, + render: render + }); +}; +ol.inherits(ol.control.ZoomSlider, ol.control.Control); + + +/** + * @inheritDoc + */ +ol.control.ZoomSlider.prototype.disposeInternal = function() { + this.dragger_.dispose(); + ol.control.Control.prototype.disposeInternal.call(this); +}; + + +/** + * The enum for available directions. + * + * @enum {number} + */ +ol.control.ZoomSlider.direction = { + VERTICAL: 0, + HORIZONTAL: 1 +}; + + +/** + * @inheritDoc + */ +ol.control.ZoomSlider.prototype.setMap = function(map) { + ol.control.Control.prototype.setMap.call(this, map); + if (map) { + map.render(); + } +}; + + +/** + * Initializes the slider element. This will determine and set this controls + * direction_ and also constrain the dragging of the thumb to always be within + * the bounds of the container. + * + * @private + */ +ol.control.ZoomSlider.prototype.initSlider_ = function() { + var container = this.element; + var containerSize = { + width: container.offsetWidth, height: container.offsetHeight + }; + + var thumb = container.firstElementChild; + var computedStyle = ol.global.getComputedStyle(thumb); + var thumbWidth = thumb.offsetWidth + + parseFloat(computedStyle['marginRight']) + + parseFloat(computedStyle['marginLeft']); + var thumbHeight = thumb.offsetHeight + + parseFloat(computedStyle['marginTop']) + + parseFloat(computedStyle['marginBottom']); + this.thumbSize_ = [thumbWidth, thumbHeight]; + + if (containerSize.width > containerSize.height) { + this.direction_ = ol.control.ZoomSlider.direction.HORIZONTAL; + this.widthLimit_ = containerSize.width - thumbWidth; + } else { + this.direction_ = ol.control.ZoomSlider.direction.VERTICAL; + this.heightLimit_ = containerSize.height - thumbHeight; + } + this.sliderInitialized_ = true; +}; + + +/** + * Update the zoomslider element. + * @param {ol.MapEvent} mapEvent Map event. + * @this {ol.control.ZoomSlider} + * @api + */ +ol.control.ZoomSlider.render = function(mapEvent) { + if (!mapEvent.frameState) { + return; + } + goog.asserts.assert(mapEvent.frameState.viewState, + 'viewState should be defined'); + if (!this.sliderInitialized_) { + this.initSlider_(); + } + var res = mapEvent.frameState.viewState.resolution; + if (res !== this.currentResolution_) { + this.currentResolution_ = res; + this.setThumbPosition_(res); + } +}; + + +/** + * @param {Event} event The browser event to handle. + * @private + */ +ol.control.ZoomSlider.prototype.handleContainerClick_ = function(event) { + var map = this.getMap(); + var view = map.getView(); + var currentResolution = view.getResolution(); + goog.asserts.assert(currentResolution, + 'currentResolution should be defined'); + map.beforeRender(ol.animation.zoom({ + resolution: currentResolution, + duration: this.duration_, + easing: ol.easing.easeOut + })); + var relativePosition = this.getRelativePosition_( + event.offsetX - this.thumbSize_[0] / 2, + event.offsetY - this.thumbSize_[1] / 2); + var resolution = this.getResolutionForPosition_(relativePosition); + view.setResolution(view.constrainResolution(resolution)); +}; + + +/** + * Handle dragger start events. + * @param {ol.pointer.PointerEvent} event The drag event. + * @private + */ +ol.control.ZoomSlider.prototype.handleDraggerStart_ = function(event) { + if (!this.dragging_ && + event.originalEvent.target === this.element.firstElementChild) { + this.getMap().getView().setHint(ol.ViewHint.INTERACTING, 1); + this.previousX_ = event.clientX; + this.previousY_ = event.clientY; + this.dragging_ = true; + + if (this.dragListenerKeys_.length === 0) { + var drag = this.handleDraggerDrag_; + var end = this.handleDraggerEnd_; + this.dragListenerKeys_.push( + ol.events.listen(document, ol.events.EventType.MOUSEMOVE, drag, this), + ol.events.listen(document, ol.events.EventType.TOUCHMOVE, drag, this), + ol.events.listen(document, ol.pointer.EventType.POINTERMOVE, drag, this), + ol.events.listen(document, ol.events.EventType.MOUSEUP, end, this), + ol.events.listen(document, ol.events.EventType.TOUCHEND, end, this), + ol.events.listen(document, ol.pointer.EventType.POINTERUP, end, this) + ); + } + } +}; + + +/** + * Handle dragger drag events. + * + * @param {ol.pointer.PointerEvent|Event} event The drag event. + * @private + */ +ol.control.ZoomSlider.prototype.handleDraggerDrag_ = function(event) { + if (this.dragging_) { + var element = this.element.firstElementChild; + var deltaX = event.clientX - this.previousX_ + parseInt(element.style.left, 10); + var deltaY = event.clientY - this.previousY_ + parseInt(element.style.top, 10); + var relativePosition = this.getRelativePosition_(deltaX, deltaY); + this.currentResolution_ = this.getResolutionForPosition_(relativePosition); + this.getMap().getView().setResolution(this.currentResolution_); + this.setThumbPosition_(this.currentResolution_); + this.previousX_ = event.clientX; + this.previousY_ = event.clientY; + } +}; + + +/** + * Handle dragger end events. + * @param {ol.pointer.PointerEvent|Event} event The drag event. + * @private + */ +ol.control.ZoomSlider.prototype.handleDraggerEnd_ = function(event) { + if (this.dragging_) { + var map = this.getMap(); + var view = map.getView(); + view.setHint(ol.ViewHint.INTERACTING, -1); + goog.asserts.assert(this.currentResolution_, + 'this.currentResolution_ should be defined'); + map.beforeRender(ol.animation.zoom({ + resolution: this.currentResolution_, + duration: this.duration_, + easing: ol.easing.easeOut + })); + var resolution = view.constrainResolution(this.currentResolution_); + view.setResolution(resolution); + this.dragging_ = false; + this.previousX_ = undefined; + this.previousY_ = undefined; + this.dragListenerKeys_.forEach(ol.events.unlistenByKey); + this.dragListenerKeys_.length = 0; + } +}; + + +/** + * Positions the thumb inside its container according to the given resolution. + * + * @param {number} res The res. + * @private + */ +ol.control.ZoomSlider.prototype.setThumbPosition_ = function(res) { + var position = this.getPositionForResolution_(res); + var thumb = this.element.firstElementChild; + + if (this.direction_ == ol.control.ZoomSlider.direction.HORIZONTAL) { + thumb.style.left = this.widthLimit_ * position + 'px'; + } else { + thumb.style.top = this.heightLimit_ * position + 'px'; + } +}; + + +/** + * Calculates the relative position of the thumb given x and y offsets. The + * relative position scales from 0 to 1. The x and y offsets are assumed to be + * in pixel units within the dragger limits. + * + * @param {number} x Pixel position relative to the left of the slider. + * @param {number} y Pixel position relative to the top of the slider. + * @return {number} The relative position of the thumb. + * @private + */ +ol.control.ZoomSlider.prototype.getRelativePosition_ = function(x, y) { + var amount; + if (this.direction_ === ol.control.ZoomSlider.direction.HORIZONTAL) { + amount = x / this.widthLimit_; + } else { + amount = y / this.heightLimit_; + } + return ol.math.clamp(amount, 0, 1); +}; + + +/** + * Calculates the corresponding resolution of the thumb given its relative + * position (where 0 is the minimum and 1 is the maximum). + * + * @param {number} position The relative position of the thumb. + * @return {number} The corresponding resolution. + * @private + */ +ol.control.ZoomSlider.prototype.getResolutionForPosition_ = function(position) { + var fn = this.getMap().getView().getResolutionForValueFunction(); + return fn(1 - position); +}; + + +/** + * Determines the relative position of the slider for the given resolution. A + * relative position of 0 corresponds to the minimum view resolution. A + * relative position of 1 corresponds to the maximum view resolution. + * + * @param {number} res The resolution. + * @return {number} The relative position value (between 0 and 1). + * @private + */ +ol.control.ZoomSlider.prototype.getPositionForResolution_ = function(res) { + var fn = this.getMap().getView().getValueForResolutionFunction(); + return 1 - fn(res); +}; + +goog.provide('ol.control.ZoomToExtent'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.control.Control'); +goog.require('ol.css'); + + +/** + * @classdesc + * A button control which, when pressed, changes the map view to a specific + * extent. To style this control use the css selector `.ol-zoom-extent`. + * + * @constructor + * @extends {ol.control.Control} + * @param {olx.control.ZoomToExtentOptions=} opt_options Options. + * @api stable + */ +ol.control.ZoomToExtent = function(opt_options) { + var options = opt_options ? opt_options : {}; + + /** + * @type {ol.Extent} + * @private + */ + this.extent_ = options.extent ? options.extent : null; + + var className = options.className !== undefined ? options.className : + 'ol-zoom-extent'; + + var label = options.label !== undefined ? options.label : 'E'; + var tipLabel = options.tipLabel !== undefined ? + options.tipLabel : 'Fit to extent'; + var button = document.createElement('button'); + button.setAttribute('type', 'button'); + button.title = tipLabel; + button.appendChild( + typeof label === 'string' ? document.createTextNode(label) : label + ); + + ol.events.listen(button, ol.events.EventType.CLICK, + this.handleClick_, this); + + var cssClasses = className + ' ' + ol.css.CLASS_UNSELECTABLE + ' ' + + ol.css.CLASS_CONTROL; + var element = document.createElement('div'); + element.className = cssClasses; + element.appendChild(button); + + ol.control.Control.call(this, { + element: element, + target: options.target + }); +}; +ol.inherits(ol.control.ZoomToExtent, ol.control.Control); + + +/** + * @param {Event} event The event to handle + * @private + */ +ol.control.ZoomToExtent.prototype.handleClick_ = function(event) { + event.preventDefault(); + this.handleZoomToExtent_(); +}; + + +/** + * @private + */ +ol.control.ZoomToExtent.prototype.handleZoomToExtent_ = function() { + var map = this.getMap(); + var view = map.getView(); + var extent = !this.extent_ ? view.getProjection().getExtent() : this.extent_; + var size = map.getSize(); + goog.asserts.assert(size, 'size should be defined'); + view.fit(extent, size); +}; + +goog.provide('ol.DeviceOrientation'); + +goog.require('ol.events'); +goog.require('ol'); +goog.require('ol.Object'); +goog.require('ol.has'); +goog.require('ol.math'); + + +/** + * @enum {string} + */ +ol.DeviceOrientationProperty = { + ALPHA: 'alpha', + BETA: 'beta', + GAMMA: 'gamma', + HEADING: 'heading', + TRACKING: 'tracking' +}; + + +/** + * @classdesc + * The ol.DeviceOrientation class provides access to information from + * DeviceOrientation events. See the [HTML 5 DeviceOrientation Specification]( + * http://www.w3.org/TR/orientation-event/) for more details. + * + * Many new computers, and especially mobile phones + * and tablets, provide hardware support for device orientation. Web + * developers targeting mobile devices will be especially interested in this + * class. + * + * Device orientation data are relative to a common starting point. For mobile + * devices, the starting point is to lay your phone face up on a table with the + * top of the phone pointing north. This represents the zero state. All + * angles are then relative to this state. For computers, it is the same except + * the screen is open at 90 degrees. + * + * Device orientation is reported as three angles - `alpha`, `beta`, and + * `gamma` - relative to the starting position along the three planar axes X, Y + * and Z. The X axis runs from the left edge to the right edge through the + * middle of the device. Similarly, the Y axis runs from the bottom to the top + * of the device through the middle. The Z axis runs from the back to the front + * through the middle. In the starting position, the X axis points to the + * right, the Y axis points away from you and the Z axis points straight up + * from the device lying flat. + * + * The three angles representing the device orientation are relative to the + * three axes. `alpha` indicates how much the device has been rotated around the + * Z axis, which is commonly interpreted as the compass heading (see note + * below). `beta` indicates how much the device has been rotated around the X + * axis, or how much it is tilted from front to back. `gamma` indicates how + * much the device has been rotated around the Y axis, or how much it is tilted + * from left to right. + * + * For most browsers, the `alpha` value returns the compass heading so if the + * device points north, it will be 0. With Safari on iOS, the 0 value of + * `alpha` is calculated from when device orientation was first requested. + * ol.DeviceOrientation provides the `heading` property which normalizes this + * behavior across all browsers for you. + * + * It is important to note that the HTML 5 DeviceOrientation specification + * indicates that `alpha`, `beta` and `gamma` are in degrees while the + * equivalent properties in ol.DeviceOrientation are in radians for consistency + * with all other uses of angles throughout OpenLayers. + * + * To get notified of device orientation changes, register a listener for the + * generic `change` event on your `ol.DeviceOrientation` instance. + * + * @see {@link http://www.w3.org/TR/orientation-event/} + * + * @constructor + * @extends {ol.Object} + * @param {olx.DeviceOrientationOptions=} opt_options Options. + * @api + */ +ol.DeviceOrientation = function(opt_options) { + + ol.Object.call(this); + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {?ol.EventsKey} + */ + this.listenerKey_ = null; + + ol.events.listen(this, + ol.Object.getChangeEventType(ol.DeviceOrientationProperty.TRACKING), + this.handleTrackingChanged_, this); + + this.setTracking(options.tracking !== undefined ? options.tracking : false); + +}; +ol.inherits(ol.DeviceOrientation, ol.Object); + + +/** + * @inheritDoc + */ +ol.DeviceOrientation.prototype.disposeInternal = function() { + this.setTracking(false); + ol.Object.prototype.disposeInternal.call(this); +}; + + +/** + * @private + * @param {Event} originalEvent Event. + */ +ol.DeviceOrientation.prototype.orientationChange_ = function(originalEvent) { + var event = /** @type {DeviceOrientationEvent} */ (originalEvent); + if (event.alpha !== null) { + var alpha = ol.math.toRadians(event.alpha); + this.set(ol.DeviceOrientationProperty.ALPHA, alpha); + // event.absolute is undefined in iOS. + if (typeof event.absolute === 'boolean' && event.absolute) { + this.set(ol.DeviceOrientationProperty.HEADING, alpha); + } else if (goog.isNumber(event.webkitCompassHeading) && + event.webkitCompassAccuracy != -1) { + var heading = ol.math.toRadians(event.webkitCompassHeading); + this.set(ol.DeviceOrientationProperty.HEADING, heading); + } + } + if (event.beta !== null) { + this.set(ol.DeviceOrientationProperty.BETA, + ol.math.toRadians(event.beta)); + } + if (event.gamma !== null) { + this.set(ol.DeviceOrientationProperty.GAMMA, + ol.math.toRadians(event.gamma)); + } + this.changed(); +}; + + +/** + * Rotation around the device z-axis (in radians). + * @return {number|undefined} The euler angle in radians of the device from the + * standard Z axis. + * @observable + * @api + */ +ol.DeviceOrientation.prototype.getAlpha = function() { + return /** @type {number|undefined} */ ( + this.get(ol.DeviceOrientationProperty.ALPHA)); +}; + + +/** + * Rotation around the device x-axis (in radians). + * @return {number|undefined} The euler angle in radians of the device from the + * planar X axis. + * @observable + * @api + */ +ol.DeviceOrientation.prototype.getBeta = function() { + return /** @type {number|undefined} */ ( + this.get(ol.DeviceOrientationProperty.BETA)); +}; + + +/** + * Rotation around the device y-axis (in radians). + * @return {number|undefined} The euler angle in radians of the device from the + * planar Y axis. + * @observable + * @api + */ +ol.DeviceOrientation.prototype.getGamma = function() { + return /** @type {number|undefined} */ ( + this.get(ol.DeviceOrientationProperty.GAMMA)); +}; + + +/** + * The heading of the device relative to north (in radians). + * @return {number|undefined} The heading of the device relative to north, in + * radians, normalizing for different browser behavior. + * @observable + * @api + */ +ol.DeviceOrientation.prototype.getHeading = function() { + return /** @type {number|undefined} */ ( + this.get(ol.DeviceOrientationProperty.HEADING)); +}; + + +/** + * Determine if orientation is being tracked. + * @return {boolean} Changes in device orientation are being tracked. + * @observable + * @api + */ +ol.DeviceOrientation.prototype.getTracking = function() { + return /** @type {boolean} */ ( + this.get(ol.DeviceOrientationProperty.TRACKING)); +}; + + +/** + * @private + */ +ol.DeviceOrientation.prototype.handleTrackingChanged_ = function() { + if (ol.has.DEVICE_ORIENTATION) { + var tracking = this.getTracking(); + if (tracking && !this.listenerKey_) { + this.listenerKey_ = ol.events.listen(ol.global, 'deviceorientation', + this.orientationChange_, this); + } else if (!tracking && this.listenerKey_ !== null) { + ol.events.unlistenByKey(this.listenerKey_); + this.listenerKey_ = null; + } + } +}; + + +/** + * Enable or disable tracking of device orientation events. + * @param {boolean} tracking The status of tracking changes to alpha, beta and + * gamma. If true, changes are tracked and reported immediately. + * @observable + * @api + */ +ol.DeviceOrientation.prototype.setTracking = function(tracking) { + this.set(ol.DeviceOrientationProperty.TRACKING, tracking); +}; + +goog.provide('ol.format.Feature'); + +goog.require('ol.geom.Geometry'); +goog.require('ol.proj'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for feature formats. + * {ol.format.Feature} subclasses provide the ability to decode and encode + * {@link ol.Feature} objects from a variety of commonly used geospatial + * file formats. See the documentation for each format for more details. + * + * @constructor + * @api stable + */ +ol.format.Feature = function() { + + /** + * @protected + * @type {ol.proj.Projection} + */ + this.defaultDataProjection = null; + +}; + + +/** + * @return {Array.<string>} Extensions. + */ +ol.format.Feature.prototype.getExtensions = goog.abstractMethod; + + +/** + * Adds the data projection to the read options. + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Options. + * @return {olx.format.ReadOptions|undefined} Options. + * @protected + */ +ol.format.Feature.prototype.getReadOptions = function(source, opt_options) { + var options; + if (opt_options) { + options = { + dataProjection: opt_options.dataProjection ? + opt_options.dataProjection : this.readProjection(source), + featureProjection: opt_options.featureProjection + }; + } + return this.adaptOptions(options); +}; + + +/** + * Sets the `defaultDataProjection` on the options, if no `dataProjection` + * is set. + * @param {olx.format.WriteOptions|olx.format.ReadOptions|undefined} options + * Options. + * @protected + * @return {olx.format.WriteOptions|olx.format.ReadOptions|undefined} + * Updated options. + */ +ol.format.Feature.prototype.adaptOptions = function(options) { + var updatedOptions; + if (options) { + updatedOptions = { + featureProjection: options.featureProjection, + dataProjection: options.dataProjection ? + options.dataProjection : this.defaultDataProjection, + rightHanded: options.rightHanded + }; + if (options.decimals) { + updatedOptions.decimals = options.decimals; + } + } + return updatedOptions; +}; + + +/** + * @return {ol.format.FormatType} Format. + */ +ol.format.Feature.prototype.getType = goog.abstractMethod; + + +/** + * Read a single feature from a source. + * + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + */ +ol.format.Feature.prototype.readFeature = goog.abstractMethod; + + +/** + * Read all features from a source. + * + * @param {Document|Node|ArrayBuffer|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + */ +ol.format.Feature.prototype.readFeatures = goog.abstractMethod; + + +/** + * Read a single geometry from a source. + * + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.Feature.prototype.readGeometry = goog.abstractMethod; + + +/** + * Read the projection from a source. + * + * @param {Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + */ +ol.format.Feature.prototype.readProjection = goog.abstractMethod; + + +/** + * Encode a feature in this format. + * + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} Result. + */ +ol.format.Feature.prototype.writeFeature = goog.abstractMethod; + + +/** + * Encode an array of features in this format. + * + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} Result. + */ +ol.format.Feature.prototype.writeFeatures = goog.abstractMethod; + + +/** + * Write a single geometry in this format. + * + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} Result. + */ +ol.format.Feature.prototype.writeGeometry = goog.abstractMethod; + + +/** + * @param {ol.geom.Geometry|ol.Extent} geometry Geometry. + * @param {boolean} write Set to true for writing, false for reading. + * @param {(olx.format.WriteOptions|olx.format.ReadOptions)=} opt_options + * Options. + * @return {ol.geom.Geometry|ol.Extent} Transformed geometry. + * @protected + */ +ol.format.Feature.transformWithOptions = function( + geometry, write, opt_options) { + var featureProjection = opt_options ? + ol.proj.get(opt_options.featureProjection) : null; + var dataProjection = opt_options ? + ol.proj.get(opt_options.dataProjection) : null; + /** + * @type {ol.geom.Geometry|ol.Extent} + */ + var transformed; + if (featureProjection && dataProjection && + !ol.proj.equivalent(featureProjection, dataProjection)) { + if (geometry instanceof ol.geom.Geometry) { + transformed = (write ? geometry.clone() : geometry).transform( + write ? featureProjection : dataProjection, + write ? dataProjection : featureProjection); + } else { + // FIXME this is necessary because ol.format.GML treats extents + // as geometries + transformed = ol.proj.transformExtent( + write ? geometry.slice() : geometry, + write ? featureProjection : dataProjection, + write ? dataProjection : featureProjection); + } + } else { + transformed = geometry; + } + if (write && opt_options && opt_options.decimals) { + var power = Math.pow(10, opt_options.decimals); + // if decimals option on write, round each coordinate appropriately + /** + * @param {Array.<number>} coordinates Coordinates. + * @return {Array.<number>} Transformed coordinates. + */ + var transform = function(coordinates) { + for (var i = 0, ii = coordinates.length; i < ii; ++i) { + coordinates[i] = Math.round(coordinates[i] * power) / power; + } + return coordinates; + }; + if (Array.isArray(transformed)) { + transform(transformed); + } else { + transformed.applyTransform(transform); + } + } + return transformed; +}; + +goog.provide('ol.format.JSONFeature'); + +goog.require('goog.asserts'); +goog.require('ol.format.Feature'); +goog.require('ol.format.FormatType'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for JSON feature formats. + * + * @constructor + * @extends {ol.format.Feature} + */ +ol.format.JSONFeature = function() { + ol.format.Feature.call(this); +}; +ol.inherits(ol.format.JSONFeature, ol.format.Feature); + + +/** + * @param {Document|Node|Object|string} source Source. + * @private + * @return {Object} Object. + */ +ol.format.JSONFeature.prototype.getObject_ = function(source) { + if (goog.isObject(source)) { + return source; + } else if (typeof source === 'string') { + var object = JSON.parse(source); + return object ? /** @type {Object} */ (object) : null; + } else { + goog.asserts.fail(); + return null; + } +}; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.getType = function() { + return ol.format.FormatType.JSON; +}; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.readFeature = function(source, opt_options) { + return this.readFeatureFromObject( + this.getObject_(source), this.getReadOptions(source, opt_options)); +}; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.readFeatures = function(source, opt_options) { + return this.readFeaturesFromObject( + this.getObject_(source), this.getReadOptions(source, opt_options)); +}; + + +/** + * @param {Object} object Object. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @protected + * @return {ol.Feature} Feature. + */ +ol.format.JSONFeature.prototype.readFeatureFromObject = goog.abstractMethod; + + +/** + * @param {Object} object Object. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @protected + * @return {Array.<ol.Feature>} Features. + */ +ol.format.JSONFeature.prototype.readFeaturesFromObject = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.readGeometry = function(source, opt_options) { + return this.readGeometryFromObject( + this.getObject_(source), this.getReadOptions(source, opt_options)); +}; + + +/** + * @param {Object} object Object. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @protected + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.JSONFeature.prototype.readGeometryFromObject = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.readProjection = function(source) { + return this.readProjectionFromObject(this.getObject_(source)); +}; + + +/** + * @param {Object} object Object. + * @protected + * @return {ol.proj.Projection} Projection. + */ +ol.format.JSONFeature.prototype.readProjectionFromObject = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.writeFeature = function(feature, opt_options) { + return JSON.stringify(this.writeFeatureObject(feature, opt_options)); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ +ol.format.JSONFeature.prototype.writeFeatureObject = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.writeFeatures = function(features, opt_options) { + return JSON.stringify(this.writeFeaturesObject(features, opt_options)); +}; + + +/** + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ +ol.format.JSONFeature.prototype.writeFeaturesObject = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.JSONFeature.prototype.writeGeometry = function(geometry, opt_options) { + return JSON.stringify(this.writeGeometryObject(geometry, opt_options)); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {Object} Object. + */ +ol.format.JSONFeature.prototype.writeGeometryObject = goog.abstractMethod; + +goog.provide('ol.geom.flat.interpolate'); + +goog.require('goog.asserts'); +goog.require('ol.array'); +goog.require('ol.math'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} fraction Fraction. + * @param {Array.<number>=} opt_dest Destination. + * @return {Array.<number>} Destination. + */ +ol.geom.flat.interpolate.lineString = function(flatCoordinates, offset, end, stride, fraction, opt_dest) { + // FIXME does not work when vertices are repeated + // FIXME interpolate extra dimensions + goog.asserts.assert(0 <= fraction && fraction <= 1, + 'fraction should be in between 0 and 1'); + var pointX = NaN; + var pointY = NaN; + var n = (end - offset) / stride; + if (n === 0) { + goog.asserts.fail('n cannot be 0'); + } else if (n == 1) { + pointX = flatCoordinates[offset]; + pointY = flatCoordinates[offset + 1]; + } else if (n == 2) { + pointX = (1 - fraction) * flatCoordinates[offset] + + fraction * flatCoordinates[offset + stride]; + pointY = (1 - fraction) * flatCoordinates[offset + 1] + + fraction * flatCoordinates[offset + stride + 1]; + } else { + var x1 = flatCoordinates[offset]; + var y1 = flatCoordinates[offset + 1]; + var length = 0; + var cumulativeLengths = [0]; + var i; + for (i = offset + stride; i < end; i += stride) { + var x2 = flatCoordinates[i]; + var y2 = flatCoordinates[i + 1]; + length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + cumulativeLengths.push(length); + x1 = x2; + y1 = y2; + } + var target = fraction * length; + var index = ol.array.binarySearch(cumulativeLengths, target); + if (index < 0) { + var t = (target - cumulativeLengths[-index - 2]) / + (cumulativeLengths[-index - 1] - cumulativeLengths[-index - 2]); + var o = offset + (-index - 2) * stride; + pointX = ol.math.lerp( + flatCoordinates[o], flatCoordinates[o + stride], t); + pointY = ol.math.lerp( + flatCoordinates[o + 1], flatCoordinates[o + stride + 1], t); + } else { + pointX = flatCoordinates[offset + index * stride]; + pointY = flatCoordinates[offset + index * stride + 1]; + } + } + if (opt_dest) { + opt_dest[0] = pointX; + opt_dest[1] = pointY; + return opt_dest; + } else { + return [pointX, pointY]; + } +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {number} m M. + * @param {boolean} extrapolate Extrapolate. + * @return {ol.Coordinate} Coordinate. + */ +ol.geom.flat.lineStringCoordinateAtM = function(flatCoordinates, offset, end, stride, m, extrapolate) { + if (end == offset) { + return null; + } + var coordinate; + if (m < flatCoordinates[offset + stride - 1]) { + if (extrapolate) { + coordinate = flatCoordinates.slice(offset, offset + stride); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } else if (flatCoordinates[end - 1] < m) { + if (extrapolate) { + coordinate = flatCoordinates.slice(end - stride, end); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } + // FIXME use O(1) search + if (m == flatCoordinates[offset + stride - 1]) { + return flatCoordinates.slice(offset, offset + stride); + } + var lo = offset / stride; + var hi = end / stride; + while (lo < hi) { + var mid = (lo + hi) >> 1; + if (m < flatCoordinates[(mid + 1) * stride - 1]) { + hi = mid; + } else { + lo = mid + 1; + } + } + var m0 = flatCoordinates[lo * stride - 1]; + if (m == m0) { + return flatCoordinates.slice((lo - 1) * stride, (lo - 1) * stride + stride); + } + var m1 = flatCoordinates[(lo + 1) * stride - 1]; + goog.asserts.assert(m0 < m, 'm0 should be less than m'); + goog.asserts.assert(m <= m1, 'm should be less than or equal to m1'); + var t = (m - m0) / (m1 - m0); + coordinate = []; + var i; + for (i = 0; i < stride - 1; ++i) { + coordinate.push(ol.math.lerp(flatCoordinates[(lo - 1) * stride + i], + flatCoordinates[lo * stride + i], t)); + } + coordinate.push(m); + goog.asserts.assert(coordinate.length == stride, + 'length of coordinate array should match stride'); + return coordinate; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<number>} ends Ends. + * @param {number} stride Stride. + * @param {number} m M. + * @param {boolean} extrapolate Extrapolate. + * @param {boolean} interpolate Interpolate. + * @return {ol.Coordinate} Coordinate. + */ +ol.geom.flat.lineStringsCoordinateAtM = function( + flatCoordinates, offset, ends, stride, m, extrapolate, interpolate) { + if (interpolate) { + return ol.geom.flat.lineStringCoordinateAtM( + flatCoordinates, offset, ends[ends.length - 1], stride, m, extrapolate); + } + var coordinate; + if (m < flatCoordinates[stride - 1]) { + if (extrapolate) { + coordinate = flatCoordinates.slice(0, stride); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } + if (flatCoordinates[flatCoordinates.length - 1] < m) { + if (extrapolate) { + coordinate = flatCoordinates.slice(flatCoordinates.length - stride); + coordinate[stride - 1] = m; + return coordinate; + } else { + return null; + } + } + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + if (offset == end) { + continue; + } + if (m < flatCoordinates[offset + stride - 1]) { + return null; + } else if (m <= flatCoordinates[end - 1]) { + return ol.geom.flat.lineStringCoordinateAtM( + flatCoordinates, offset, end, stride, m, false); + } + offset = end; + } + goog.asserts.fail( + 'ol.geom.flat.lineStringsCoordinateAtM should have returned'); + return null; +}; + +goog.provide('ol.geom.flat.length'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {number} Length. + */ +ol.geom.flat.length.lineString = function(flatCoordinates, offset, end, stride) { + var x1 = flatCoordinates[offset]; + var y1 = flatCoordinates[offset + 1]; + var length = 0; + var i; + for (i = offset + stride; i < end; i += stride) { + var x2 = flatCoordinates[i]; + var y2 = flatCoordinates[i + 1]; + length += Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); + x1 = x2; + y1 = y2; + } + return length; +}; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @return {number} Perimeter. + */ +ol.geom.flat.length.linearRing = function(flatCoordinates, offset, end, stride) { + var perimeter = + ol.geom.flat.length.lineString(flatCoordinates, offset, end, stride); + var dx = flatCoordinates[end - stride] - flatCoordinates[offset]; + var dy = flatCoordinates[end - stride + 1] - flatCoordinates[offset + 1]; + perimeter += Math.sqrt(dx * dx + dy * dy); + return perimeter; +}; + +goog.provide('ol.geom.LineString'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.closest'); +goog.require('ol.geom.flat.deflate'); +goog.require('ol.geom.flat.inflate'); +goog.require('ol.geom.flat.interpolate'); +goog.require('ol.geom.flat.intersectsextent'); +goog.require('ol.geom.flat.length'); +goog.require('ol.geom.flat.segments'); +goog.require('ol.geom.flat.simplify'); + + +/** + * @classdesc + * Linestring geometry. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.LineString = function(coordinates, opt_layout) { + + ol.geom.SimpleGeometry.call(this); + + /** + * @private + * @type {ol.Coordinate} + */ + this.flatMidpoint_ = null; + + /** + * @private + * @type {number} + */ + this.flatMidpointRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; + + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; + + this.setCoordinates(coordinates, opt_layout); + +}; +ol.inherits(ol.geom.LineString, ol.geom.SimpleGeometry); + + +/** + * Append the passed coordinate to the coordinates of the linestring. + * @param {ol.Coordinate} coordinate Coordinate. + * @api stable + */ +ol.geom.LineString.prototype.appendCoordinate = function(coordinate) { + goog.asserts.assert(coordinate.length == this.stride, + 'length of coordinate array should match stride'); + if (!this.flatCoordinates) { + this.flatCoordinates = coordinate.slice(); + } else { + ol.array.extend(this.flatCoordinates, coordinate); + } + this.changed(); +}; + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.LineString} Clone. + * @api stable + */ +ol.geom.LineString.prototype.clone = function() { + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); + return lineString; +}; + + +/** + * @inheritDoc + */ +ol.geom.LineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getMaxSquaredDelta( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return ol.geom.flat.closest.getClosestPoint( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); +}; + + +/** + * Iterate over each segment, calling the provided callback. + * If the callback returns a truthy value the function returns that + * value immediately. Otherwise the function returns `false`. + * + * @param {function(this: S, ol.Coordinate, ol.Coordinate): T} callback Function + * called for each segment. + * @param {S=} opt_this The object to be used as the value of 'this' + * within callback. + * @return {T|boolean} Value. + * @template T,S + * @api + */ +ol.geom.LineString.prototype.forEachSegment = function(callback, opt_this) { + return ol.geom.flat.segments.forEach(this.flatCoordinates, 0, + this.flatCoordinates.length, this.stride, callback, opt_this); +}; + + +/** + * Returns the coordinate at `m` using linear interpolation, or `null` if no + * such coordinate exists. + * + * `opt_extrapolate` controls extrapolation beyond the range of Ms in the + * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first + * M will return the first coordinate and Ms greater than the last M will + * return the last coordinate. + * + * @param {number} m M. + * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. + * @return {ol.Coordinate} Coordinate. + * @api stable + */ +ol.geom.LineString.prototype.getCoordinateAtM = function(m, opt_extrapolate) { + if (this.layout != ol.geom.GeometryLayout.XYM && + this.layout != ol.geom.GeometryLayout.XYZM) { + return null; + } + var extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; + return ol.geom.flat.lineStringCoordinateAtM(this.flatCoordinates, 0, + this.flatCoordinates.length, this.stride, m, extrapolate); +}; + + +/** + * Return the coordinates of the linestring. + * @return {Array.<ol.Coordinate>} Coordinates. + * @api stable + */ +ol.geom.LineString.prototype.getCoordinates = function() { + return ol.geom.flat.inflate.coordinates( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); +}; + + +/** + * Return the coordinate at the provided fraction along the linestring. + * The `fraction` is a number between 0 and 1, where 0 is the start of the + * linestring and 1 is the end. + * @param {number} fraction Fraction. + * @param {ol.Coordinate=} opt_dest Optional coordinate whose values will + * be modified. If not provided, a new coordinate will be returned. + * @return {ol.Coordinate} Coordinate of the interpolated point. + * @api + */ +ol.geom.LineString.prototype.getCoordinateAt = function(fraction, opt_dest) { + return ol.geom.flat.interpolate.lineString( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + fraction, opt_dest); +}; + + +/** + * Return the length of the linestring on projected plane. + * @return {number} Length (on projected plane). + * @api stable + */ +ol.geom.LineString.prototype.getLength = function() { + return ol.geom.flat.length.lineString( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); +}; + + +/** + * @return {Array.<number>} Flat midpoint. + */ +ol.geom.LineString.prototype.getFlatMidpoint = function() { + if (this.flatMidpointRevision_ != this.getRevision()) { + this.flatMidpoint_ = this.getCoordinateAt(0.5, this.flatMidpoint_); + this.flatMidpointRevision_ = this.getRevision(); + } + return this.flatMidpoint_; +}; + + +/** + * @inheritDoc + */ +ol.geom.LineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { + var simplifiedFlatCoordinates = []; + simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeucker( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + squaredTolerance, simplifiedFlatCoordinates, 0); + var simplifiedLineString = new ol.geom.LineString(null); + simplifiedLineString.setFlatCoordinates( + ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates); + return simplifiedLineString; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.LineString.prototype.getType = function() { + return ol.geom.GeometryType.LINE_STRING; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.LineString.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.lineString( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride, + extent); +}; + + +/** + * Set the coordinates of the linestring. + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.LineString.prototype.setCoordinates = function(coordinates, opt_layout) { + if (!coordinates) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); + } else { + this.setLayout(opt_layout, coordinates, 1); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = ol.geom.flat.deflate.coordinates( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + */ +ol.geom.LineString.prototype.setFlatCoordinates = function(layout, flatCoordinates) { + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.changed(); +}; + +goog.provide('ol.geom.MultiLineString'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.closest'); +goog.require('ol.geom.flat.deflate'); +goog.require('ol.geom.flat.inflate'); +goog.require('ol.geom.flat.interpolate'); +goog.require('ol.geom.flat.intersectsextent'); +goog.require('ol.geom.flat.simplify'); + + +/** + * @classdesc + * Multi-linestring geometry. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.MultiLineString = function(coordinates, opt_layout) { + + ol.geom.SimpleGeometry.call(this); + + /** + * @type {Array.<number>} + * @private + */ + this.ends_ = []; + + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; + + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; + + this.setCoordinates(coordinates, opt_layout); + +}; +ol.inherits(ol.geom.MultiLineString, ol.geom.SimpleGeometry); + + +/** + * Append the passed linestring to the multilinestring. + * @param {ol.geom.LineString} lineString LineString. + * @api stable + */ +ol.geom.MultiLineString.prototype.appendLineString = function(lineString) { + goog.asserts.assert(lineString.getLayout() == this.layout, + 'layout of lineString should match the layout'); + if (!this.flatCoordinates) { + this.flatCoordinates = lineString.getFlatCoordinates().slice(); + } else { + ol.array.extend( + this.flatCoordinates, lineString.getFlatCoordinates().slice()); + } + this.ends_.push(this.flatCoordinates.length); + this.changed(); +}; + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.MultiLineString} Clone. + * @api stable + */ +ol.geom.MultiLineString.prototype.clone = function() { + var multiLineString = new ol.geom.MultiLineString(null); + multiLineString.setFlatCoordinates( + this.layout, this.flatCoordinates.slice(), this.ends_.slice()); + return multiLineString; +}; + + +/** + * @inheritDoc + */ +ol.geom.MultiLineString.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getsMaxSquaredDelta( + this.flatCoordinates, 0, this.ends_, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return ol.geom.flat.closest.getsClosestPoint( + this.flatCoordinates, 0, this.ends_, this.stride, + this.maxDelta_, false, x, y, closestPoint, minSquaredDistance); +}; + + +/** + * Returns the coordinate at `m` using linear interpolation, or `null` if no + * such coordinate exists. + * + * `opt_extrapolate` controls extrapolation beyond the range of Ms in the + * MultiLineString. If `opt_extrapolate` is `true` then Ms less than the first + * M will return the first coordinate and Ms greater than the last M will + * return the last coordinate. + * + * `opt_interpolate` controls interpolation between consecutive LineStrings + * within the MultiLineString. If `opt_interpolate` is `true` the coordinates + * will be linearly interpolated between the last coordinate of one LineString + * and the first coordinate of the next LineString. If `opt_interpolate` is + * `false` then the function will return `null` for Ms falling between + * LineStrings. + * + * @param {number} m M. + * @param {boolean=} opt_extrapolate Extrapolate. Default is `false`. + * @param {boolean=} opt_interpolate Interpolate. Default is `false`. + * @return {ol.Coordinate} Coordinate. + * @api stable + */ +ol.geom.MultiLineString.prototype.getCoordinateAtM = function(m, opt_extrapolate, opt_interpolate) { + if ((this.layout != ol.geom.GeometryLayout.XYM && + this.layout != ol.geom.GeometryLayout.XYZM) || + this.flatCoordinates.length === 0) { + return null; + } + var extrapolate = opt_extrapolate !== undefined ? opt_extrapolate : false; + var interpolate = opt_interpolate !== undefined ? opt_interpolate : false; + return ol.geom.flat.lineStringsCoordinateAtM(this.flatCoordinates, 0, + this.ends_, this.stride, m, extrapolate, interpolate); +}; + + +/** + * Return the coordinates of the multilinestring. + * @return {Array.<Array.<ol.Coordinate>>} Coordinates. + * @api stable + */ +ol.geom.MultiLineString.prototype.getCoordinates = function() { + return ol.geom.flat.inflate.coordinatess( + this.flatCoordinates, 0, this.ends_, this.stride); +}; + + +/** + * @return {Array.<number>} Ends. + */ +ol.geom.MultiLineString.prototype.getEnds = function() { + return this.ends_; +}; + + +/** + * Return the linestring at the specified index. + * @param {number} index Index. + * @return {ol.geom.LineString} LineString. + * @api stable + */ +ol.geom.MultiLineString.prototype.getLineString = function(index) { + goog.asserts.assert(0 <= index && index < this.ends_.length, + 'index should be in between 0 and length of the this.ends_ array'); + if (index < 0 || this.ends_.length <= index) { + return null; + } + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(this.layout, this.flatCoordinates.slice( + index === 0 ? 0 : this.ends_[index - 1], this.ends_[index])); + return lineString; +}; + + +/** + * Return the linestrings of this multilinestring. + * @return {Array.<ol.geom.LineString>} LineStrings. + * @api stable + */ +ol.geom.MultiLineString.prototype.getLineStrings = function() { + var flatCoordinates = this.flatCoordinates; + var ends = this.ends_; + var layout = this.layout; + /** @type {Array.<ol.geom.LineString>} */ + var lineStrings = []; + var offset = 0; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(layout, flatCoordinates.slice(offset, end)); + lineStrings.push(lineString); + offset = end; + } + return lineStrings; +}; + + +/** + * @return {Array.<number>} Flat midpoints. + */ +ol.geom.MultiLineString.prototype.getFlatMidpoints = function() { + var midpoints = []; + var flatCoordinates = this.flatCoordinates; + var offset = 0; + var ends = this.ends_; + var stride = this.stride; + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + var end = ends[i]; + var midpoint = ol.geom.flat.interpolate.lineString( + flatCoordinates, offset, end, stride, 0.5); + ol.array.extend(midpoints, midpoint); + offset = end; + } + return midpoints; +}; + + +/** + * @inheritDoc + */ +ol.geom.MultiLineString.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { + var simplifiedFlatCoordinates = []; + var simplifiedEnds = []; + simplifiedFlatCoordinates.length = ol.geom.flat.simplify.douglasPeuckers( + this.flatCoordinates, 0, this.ends_, this.stride, squaredTolerance, + simplifiedFlatCoordinates, 0, simplifiedEnds); + var simplifiedMultiLineString = new ol.geom.MultiLineString(null); + simplifiedMultiLineString.setFlatCoordinates( + ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEnds); + return simplifiedMultiLineString; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.MultiLineString.prototype.getType = function() { + return ol.geom.GeometryType.MULTI_LINE_STRING; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.MultiLineString.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.lineStrings( + this.flatCoordinates, 0, this.ends_, this.stride, extent); +}; + + +/** + * Set the coordinates of the multilinestring. + * @param {Array.<Array.<ol.Coordinate>>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.MultiLineString.prototype.setCoordinates = function(coordinates, opt_layout) { + if (!coordinates) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.ends_); + } else { + this.setLayout(opt_layout, coordinates, 2); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + var ends = ol.geom.flat.deflate.coordinatess( + this.flatCoordinates, 0, coordinates, this.stride, this.ends_); + this.flatCoordinates.length = ends.length === 0 ? 0 : ends[ends.length - 1]; + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {Array.<number>} ends Ends. + */ +ol.geom.MultiLineString.prototype.setFlatCoordinates = function(layout, flatCoordinates, ends) { + if (!flatCoordinates) { + goog.asserts.assert(ends && ends.length === 0, + 'ends must be truthy and ends.length should be 0'); + } else if (ends.length === 0) { + goog.asserts.assert(flatCoordinates.length === 0, + 'flatCoordinates should be an empty array'); + } else { + goog.asserts.assert(flatCoordinates.length == ends[ends.length - 1], + 'length of flatCoordinates array should match the last value of ends'); + } + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.ends_ = ends; + this.changed(); +}; + + +/** + * @param {Array.<ol.geom.LineString>} lineStrings LineStrings. + */ +ol.geom.MultiLineString.prototype.setLineStrings = function(lineStrings) { + var layout = this.getLayout(); + var flatCoordinates = []; + var ends = []; + var i, ii; + for (i = 0, ii = lineStrings.length; i < ii; ++i) { + var lineString = lineStrings[i]; + if (i === 0) { + layout = lineString.getLayout(); + } else { + // FIXME better handle the case of non-matching layouts + goog.asserts.assert(lineString.getLayout() == layout, + 'layout of lineString should match layout'); + } + ol.array.extend(flatCoordinates, lineString.getFlatCoordinates()); + ends.push(flatCoordinates.length); + } + this.setFlatCoordinates(layout, flatCoordinates, ends); +}; + +goog.provide('ol.geom.MultiPoint'); + +goog.require('goog.asserts'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.deflate'); +goog.require('ol.geom.flat.inflate'); +goog.require('ol.math'); + + +/** + * @classdesc + * Multi-point geometry. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.MultiPoint = function(coordinates, opt_layout) { + ol.geom.SimpleGeometry.call(this); + this.setCoordinates(coordinates, opt_layout); +}; +ol.inherits(ol.geom.MultiPoint, ol.geom.SimpleGeometry); + + +/** + * Append the passed point to this multipoint. + * @param {ol.geom.Point} point Point. + * @api stable + */ +ol.geom.MultiPoint.prototype.appendPoint = function(point) { + goog.asserts.assert(point.getLayout() == this.layout, + 'the layout of point should match layout'); + if (!this.flatCoordinates) { + this.flatCoordinates = point.getFlatCoordinates().slice(); + } else { + ol.array.extend(this.flatCoordinates, point.getFlatCoordinates()); + } + this.changed(); +}; + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.MultiPoint} Clone. + * @api stable + */ +ol.geom.MultiPoint.prototype.clone = function() { + var multiPoint = new ol.geom.MultiPoint(null); + multiPoint.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); + return multiPoint; +}; + + +/** + * @inheritDoc + */ +ol.geom.MultiPoint.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + var flatCoordinates = this.flatCoordinates; + var stride = this.stride; + var i, ii, j; + for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + var squaredDistance = ol.math.squaredDistance( + x, y, flatCoordinates[i], flatCoordinates[i + 1]); + if (squaredDistance < minSquaredDistance) { + minSquaredDistance = squaredDistance; + for (j = 0; j < stride; ++j) { + closestPoint[j] = flatCoordinates[i + j]; + } + closestPoint.length = stride; + } + } + return minSquaredDistance; +}; + + +/** + * Return the coordinates of the multipoint. + * @return {Array.<ol.Coordinate>} Coordinates. + * @api stable + */ +ol.geom.MultiPoint.prototype.getCoordinates = function() { + return ol.geom.flat.inflate.coordinates( + this.flatCoordinates, 0, this.flatCoordinates.length, this.stride); +}; + + +/** + * Return the point at the specified index. + * @param {number} index Index. + * @return {ol.geom.Point} Point. + * @api stable + */ +ol.geom.MultiPoint.prototype.getPoint = function(index) { + var n = !this.flatCoordinates ? + 0 : this.flatCoordinates.length / this.stride; + goog.asserts.assert(0 <= index && index < n, + 'index should be in between 0 and n'); + if (index < 0 || n <= index) { + return null; + } + var point = new ol.geom.Point(null); + point.setFlatCoordinates(this.layout, this.flatCoordinates.slice( + index * this.stride, (index + 1) * this.stride)); + return point; +}; + + +/** + * Return the points of this multipoint. + * @return {Array.<ol.geom.Point>} Points. + * @api stable + */ +ol.geom.MultiPoint.prototype.getPoints = function() { + var flatCoordinates = this.flatCoordinates; + var layout = this.layout; + var stride = this.stride; + /** @type {Array.<ol.geom.Point>} */ + var points = []; + var i, ii; + for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + var point = new ol.geom.Point(null); + point.setFlatCoordinates(layout, flatCoordinates.slice(i, i + stride)); + points.push(point); + } + return points; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.MultiPoint.prototype.getType = function() { + return ol.geom.GeometryType.MULTI_POINT; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.MultiPoint.prototype.intersectsExtent = function(extent) { + var flatCoordinates = this.flatCoordinates; + var stride = this.stride; + var i, ii, x, y; + for (i = 0, ii = flatCoordinates.length; i < ii; i += stride) { + x = flatCoordinates[i]; + y = flatCoordinates[i + 1]; + if (ol.extent.containsXY(extent, x, y)) { + return true; + } + } + return false; +}; + + +/** + * Set the coordinates of the multipoint. + * @param {Array.<ol.Coordinate>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.MultiPoint.prototype.setCoordinates = function(coordinates, opt_layout) { + if (!coordinates) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); + } else { + this.setLayout(opt_layout, coordinates, 1); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + this.flatCoordinates.length = ol.geom.flat.deflate.coordinates( + this.flatCoordinates, 0, coordinates, this.stride); + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + */ +ol.geom.MultiPoint.prototype.setFlatCoordinates = function(layout, flatCoordinates) { + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.changed(); +}; + +goog.provide('ol.geom.flat.center'); + +goog.require('ol.extent'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {Array.<Array.<number>>} endss Endss. + * @param {number} stride Stride. + * @return {Array.<number>} Flat centers. + */ +ol.geom.flat.center.linearRingss = function(flatCoordinates, offset, endss, stride) { + var flatCenters = []; + var i, ii; + var extent = ol.extent.createEmpty(); + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i]; + extent = ol.extent.createOrUpdateFromFlatCoordinates( + flatCoordinates, offset, ends[0], stride); + flatCenters.push((extent[0] + extent[2]) / 2, (extent[1] + extent[3]) / 2); + offset = ends[ends.length - 1]; + } + return flatCenters; +}; + +goog.provide('ol.geom.MultiPolygon'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.area'); +goog.require('ol.geom.flat.center'); +goog.require('ol.geom.flat.closest'); +goog.require('ol.geom.flat.contains'); +goog.require('ol.geom.flat.deflate'); +goog.require('ol.geom.flat.inflate'); +goog.require('ol.geom.flat.interiorpoint'); +goog.require('ol.geom.flat.intersectsextent'); +goog.require('ol.geom.flat.orient'); +goog.require('ol.geom.flat.simplify'); + + +/** + * @classdesc + * Multi-polygon geometry. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.MultiPolygon = function(coordinates, opt_layout) { + + ol.geom.SimpleGeometry.call(this); + + /** + * @type {Array.<Array.<number>>} + * @private + */ + this.endss_ = []; + + /** + * @private + * @type {number} + */ + this.flatInteriorPointsRevision_ = -1; + + /** + * @private + * @type {Array.<number>} + */ + this.flatInteriorPoints_ = null; + + /** + * @private + * @type {number} + */ + this.maxDelta_ = -1; + + /** + * @private + * @type {number} + */ + this.maxDeltaRevision_ = -1; + + /** + * @private + * @type {number} + */ + this.orientedRevision_ = -1; + + /** + * @private + * @type {Array.<number>} + */ + this.orientedFlatCoordinates_ = null; + + this.setCoordinates(coordinates, opt_layout); + +}; +ol.inherits(ol.geom.MultiPolygon, ol.geom.SimpleGeometry); + + +/** + * Append the passed polygon to this multipolygon. + * @param {ol.geom.Polygon} polygon Polygon. + * @api stable + */ +ol.geom.MultiPolygon.prototype.appendPolygon = function(polygon) { + goog.asserts.assert(polygon.getLayout() == this.layout, + 'layout of polygon should match layout'); + /** @type {Array.<number>} */ + var ends; + if (!this.flatCoordinates) { + this.flatCoordinates = polygon.getFlatCoordinates().slice(); + ends = polygon.getEnds().slice(); + this.endss_.push(); + } else { + var offset = this.flatCoordinates.length; + ol.array.extend(this.flatCoordinates, polygon.getFlatCoordinates()); + ends = polygon.getEnds().slice(); + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + ends[i] += offset; + } + } + this.endss_.push(ends); + this.changed(); +}; + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.MultiPolygon} Clone. + * @api stable + */ +ol.geom.MultiPolygon.prototype.clone = function() { + var multiPolygon = new ol.geom.MultiPolygon(null); + + var len = this.endss_.length; + var newEndss = new Array(len); + for (var i = 0; i < len; ++i) { + newEndss[i] = this.endss_[i].slice(); + } + + multiPolygon.setFlatCoordinates( + this.layout, this.flatCoordinates.slice(), newEndss); + return multiPolygon; +}; + + +/** + * @inheritDoc + */ +ol.geom.MultiPolygon.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + if (this.maxDeltaRevision_ != this.getRevision()) { + this.maxDelta_ = Math.sqrt(ol.geom.flat.closest.getssMaxSquaredDelta( + this.flatCoordinates, 0, this.endss_, this.stride, 0)); + this.maxDeltaRevision_ = this.getRevision(); + } + return ol.geom.flat.closest.getssClosestPoint( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, + this.maxDelta_, true, x, y, closestPoint, minSquaredDistance); +}; + + +/** + * @inheritDoc + */ +ol.geom.MultiPolygon.prototype.containsXY = function(x, y) { + return ol.geom.flat.contains.linearRingssContainsXY( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, x, y); +}; + + +/** + * Return the area of the multipolygon on projected plane. + * @return {number} Area (on projected plane). + * @api stable + */ +ol.geom.MultiPolygon.prototype.getArea = function() { + return ol.geom.flat.area.linearRingss( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride); +}; + + +/** + * Get the coordinate array for this geometry. This array has the structure + * of a GeoJSON coordinate array for multi-polygons. + * + * @param {boolean=} opt_right Orient coordinates according to the right-hand + * rule (counter-clockwise for exterior and clockwise for interior rings). + * If `false`, coordinates will be oriented according to the left-hand rule + * (clockwise for exterior and counter-clockwise for interior rings). + * By default, coordinate orientation will depend on how the geometry was + * constructed. + * @return {Array.<Array.<Array.<ol.Coordinate>>>} Coordinates. + * @api stable + */ +ol.geom.MultiPolygon.prototype.getCoordinates = function(opt_right) { + var flatCoordinates; + if (opt_right !== undefined) { + flatCoordinates = this.getOrientedFlatCoordinates().slice(); + ol.geom.flat.orient.orientLinearRingss( + flatCoordinates, 0, this.endss_, this.stride, opt_right); + } else { + flatCoordinates = this.flatCoordinates; + } + + return ol.geom.flat.inflate.coordinatesss( + flatCoordinates, 0, this.endss_, this.stride); +}; + + +/** + * @return {Array.<Array.<number>>} Endss. + */ +ol.geom.MultiPolygon.prototype.getEndss = function() { + return this.endss_; +}; + + +/** + * @return {Array.<number>} Flat interior points. + */ +ol.geom.MultiPolygon.prototype.getFlatInteriorPoints = function() { + if (this.flatInteriorPointsRevision_ != this.getRevision()) { + var flatCenters = ol.geom.flat.center.linearRingss( + this.flatCoordinates, 0, this.endss_, this.stride); + this.flatInteriorPoints_ = ol.geom.flat.interiorpoint.linearRingss( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, + flatCenters); + this.flatInteriorPointsRevision_ = this.getRevision(); + } + return this.flatInteriorPoints_; +}; + + +/** + * Return the interior points as {@link ol.geom.MultiPoint multipoint}. + * @return {ol.geom.MultiPoint} Interior points. + * @api stable + */ +ol.geom.MultiPolygon.prototype.getInteriorPoints = function() { + var interiorPoints = new ol.geom.MultiPoint(null); + interiorPoints.setFlatCoordinates(ol.geom.GeometryLayout.XY, + this.getFlatInteriorPoints().slice()); + return interiorPoints; +}; + + +/** + * @return {Array.<number>} Oriented flat coordinates. + */ +ol.geom.MultiPolygon.prototype.getOrientedFlatCoordinates = function() { + if (this.orientedRevision_ != this.getRevision()) { + var flatCoordinates = this.flatCoordinates; + if (ol.geom.flat.orient.linearRingssAreOriented( + flatCoordinates, 0, this.endss_, this.stride)) { + this.orientedFlatCoordinates_ = flatCoordinates; + } else { + this.orientedFlatCoordinates_ = flatCoordinates.slice(); + this.orientedFlatCoordinates_.length = + ol.geom.flat.orient.orientLinearRingss( + this.orientedFlatCoordinates_, 0, this.endss_, this.stride); + } + this.orientedRevision_ = this.getRevision(); + } + return this.orientedFlatCoordinates_; +}; + + +/** + * @inheritDoc + */ +ol.geom.MultiPolygon.prototype.getSimplifiedGeometryInternal = function(squaredTolerance) { + var simplifiedFlatCoordinates = []; + var simplifiedEndss = []; + simplifiedFlatCoordinates.length = ol.geom.flat.simplify.quantizess( + this.flatCoordinates, 0, this.endss_, this.stride, + Math.sqrt(squaredTolerance), + simplifiedFlatCoordinates, 0, simplifiedEndss); + var simplifiedMultiPolygon = new ol.geom.MultiPolygon(null); + simplifiedMultiPolygon.setFlatCoordinates( + ol.geom.GeometryLayout.XY, simplifiedFlatCoordinates, simplifiedEndss); + return simplifiedMultiPolygon; +}; + + +/** + * Return the polygon at the specified index. + * @param {number} index Index. + * @return {ol.geom.Polygon} Polygon. + * @api stable + */ +ol.geom.MultiPolygon.prototype.getPolygon = function(index) { + goog.asserts.assert(0 <= index && index < this.endss_.length, + 'index should be in between 0 and the length of this.endss_'); + if (index < 0 || this.endss_.length <= index) { + return null; + } + var offset; + if (index === 0) { + offset = 0; + } else { + var prevEnds = this.endss_[index - 1]; + offset = prevEnds[prevEnds.length - 1]; + } + var ends = this.endss_[index].slice(); + var end = ends[ends.length - 1]; + if (offset !== 0) { + var i, ii; + for (i = 0, ii = ends.length; i < ii; ++i) { + ends[i] -= offset; + } + } + var polygon = new ol.geom.Polygon(null); + polygon.setFlatCoordinates( + this.layout, this.flatCoordinates.slice(offset, end), ends); + return polygon; +}; + + +/** + * Return the polygons of this multipolygon. + * @return {Array.<ol.geom.Polygon>} Polygons. + * @api stable + */ +ol.geom.MultiPolygon.prototype.getPolygons = function() { + var layout = this.layout; + var flatCoordinates = this.flatCoordinates; + var endss = this.endss_; + var polygons = []; + var offset = 0; + var i, ii, j, jj; + for (i = 0, ii = endss.length; i < ii; ++i) { + var ends = endss[i].slice(); + var end = ends[ends.length - 1]; + if (offset !== 0) { + for (j = 0, jj = ends.length; j < jj; ++j) { + ends[j] -= offset; + } + } + var polygon = new ol.geom.Polygon(null); + polygon.setFlatCoordinates( + layout, flatCoordinates.slice(offset, end), ends); + polygons.push(polygon); + offset = end; + } + return polygons; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.MultiPolygon.prototype.getType = function() { + return ol.geom.GeometryType.MULTI_POLYGON; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.MultiPolygon.prototype.intersectsExtent = function(extent) { + return ol.geom.flat.intersectsextent.linearRingss( + this.getOrientedFlatCoordinates(), 0, this.endss_, this.stride, extent); +}; + + +/** + * Set the coordinates of the multipolygon. + * @param {Array.<Array.<Array.<ol.Coordinate>>>} coordinates Coordinates. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api stable + */ +ol.geom.MultiPolygon.prototype.setCoordinates = function(coordinates, opt_layout) { + if (!coordinates) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null, this.endss_); + } else { + this.setLayout(opt_layout, coordinates, 3); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + var endss = ol.geom.flat.deflate.coordinatesss( + this.flatCoordinates, 0, coordinates, this.stride, this.endss_); + if (endss.length === 0) { + this.flatCoordinates.length = 0; + } else { + var lastEnds = endss[endss.length - 1]; + this.flatCoordinates.length = lastEnds.length === 0 ? + 0 : lastEnds[lastEnds.length - 1]; + } + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {Array.<Array.<number>>} endss Endss. + */ +ol.geom.MultiPolygon.prototype.setFlatCoordinates = function(layout, flatCoordinates, endss) { + goog.asserts.assert(endss, 'endss must be truthy'); + if (!flatCoordinates || flatCoordinates.length === 0) { + goog.asserts.assert(endss.length === 0, 'the length of endss should be 0'); + } else { + goog.asserts.assert(endss.length > 0, 'endss cannot be an empty array'); + var ends = endss[endss.length - 1]; + goog.asserts.assert(flatCoordinates.length == ends[ends.length - 1], + 'the length of flatCoordinates should be the last value of ends'); + } + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.endss_ = endss; + this.changed(); +}; + + +/** + * @param {Array.<ol.geom.Polygon>} polygons Polygons. + */ +ol.geom.MultiPolygon.prototype.setPolygons = function(polygons) { + var layout = this.getLayout(); + var flatCoordinates = []; + var endss = []; + var i, ii, ends; + for (i = 0, ii = polygons.length; i < ii; ++i) { + var polygon = polygons[i]; + if (i === 0) { + layout = polygon.getLayout(); + } else { + // FIXME better handle the case of non-matching layouts + goog.asserts.assert(polygon.getLayout() == layout, + 'layout of polygon should be layout'); + } + var offset = flatCoordinates.length; + ends = polygon.getEnds(); + var j, jj; + for (j = 0, jj = ends.length; j < jj; ++j) { + ends[j] += offset; + } + ol.array.extend(flatCoordinates, polygon.getFlatCoordinates()); + endss.push(ends); + } + this.setFlatCoordinates(layout, flatCoordinates, endss); +}; + +goog.provide('ol.format.EsriJSON'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.format.Feature'); +goog.require('ol.format.JSONFeature'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.geom.flat.orient'); +goog.require('ol.object'); +goog.require('ol.proj'); + + +/** + * @classdesc + * Feature format for reading and writing data in the EsriJSON format. + * + * @constructor + * @extends {ol.format.JSONFeature} + * @param {olx.format.EsriJSONOptions=} opt_options Options. + * @api + */ +ol.format.EsriJSON = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.JSONFeature.call(this); + + /** + * Name of the geometry attribute for features. + * @type {string|undefined} + * @private + */ + this.geometryName_ = options.geometryName; + +}; +ol.inherits(ol.format.EsriJSON, ol.format.JSONFeature); + + +/** + * @param {EsriJSONGeometry} object Object. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @private + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.EsriJSON.readGeometry_ = function(object, opt_options) { + if (!object) { + return null; + } + var type; + if (goog.isNumber(object.x) && goog.isNumber(object.y)) { + type = ol.geom.GeometryType.POINT; + } else if (object.points) { + type = ol.geom.GeometryType.MULTI_POINT; + } else if (object.paths) { + if (object.paths.length === 1) { + type = ol.geom.GeometryType.LINE_STRING; + } else { + type = ol.geom.GeometryType.MULTI_LINE_STRING; + } + } else if (object.rings) { + var layout = ol.format.EsriJSON.getGeometryLayout_(object); + var rings = ol.format.EsriJSON.convertRings_(object.rings, layout); + object = /** @type {EsriJSONGeometry} */(ol.object.assign({}, object)); + if (rings.length === 1) { + type = ol.geom.GeometryType.POLYGON; + object.rings = rings[0]; + } else { + type = ol.geom.GeometryType.MULTI_POLYGON; + object.rings = rings; + } + } + goog.asserts.assert(type, 'geometry type should be defined'); + var geometryReader = ol.format.EsriJSON.GEOMETRY_READERS_[type]; + goog.asserts.assert(geometryReader, + 'geometryReader should be defined'); + return /** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions( + geometryReader(object), false, opt_options)); +}; + + +/** + * Determines inner and outer rings. + * Checks if any polygons in this array contain any other polygons in this + * array. It is used for checking for holes. + * Logic inspired by: https://github.com/Esri/terraformer-arcgis-parser + * @param {Array.<!Array.<!Array.<number>>>} rings Rings. + * @param {ol.geom.GeometryLayout} layout Geometry layout. + * @private + * @return {Array.<!Array.<!Array.<number>>>} Transoformed rings. + */ +ol.format.EsriJSON.convertRings_ = function(rings, layout) { + var outerRings = []; + var holes = []; + var i, ii; + for (i = 0, ii = rings.length; i < ii; ++i) { + var flatRing = ol.array.flatten(rings[i]); + // is this ring an outer ring? is it clockwise? + var clockwise = ol.geom.flat.orient.linearRingIsClockwise(flatRing, 0, + flatRing.length, layout.length); + if (clockwise) { + outerRings.push([rings[i]]); + } else { + holes.push(rings[i]); + } + } + while (holes.length) { + var hole = holes.shift(); + var matched = false; + // loop over all outer rings and see if they contain our hole. + for (i = outerRings.length - 1; i >= 0; i--) { + var outerRing = outerRings[i][0]; + if (ol.extent.containsExtent(new ol.geom.LinearRing( + outerRing).getExtent(), + new ol.geom.LinearRing(hole).getExtent())) { + // the hole is contained push it into our polygon + outerRings[i].push(hole); + matched = true; + break; + } + } + if (!matched) { + // no outer rings contain this hole turn it into and outer + // ring (reverse it) + outerRings.push([hole.reverse()]); + } + } + return outerRings; +}; + + +/** + * @param {EsriJSONGeometry} object Object. + * @private + * @return {ol.geom.Geometry} Point. + */ +ol.format.EsriJSON.readPointGeometry_ = function(object) { + goog.asserts.assert(goog.isNumber(object.x), 'object.x should be number'); + goog.asserts.assert(goog.isNumber(object.y), 'object.y should be number'); + var point; + if (object.m !== undefined && object.z !== undefined) { + point = new ol.geom.Point([object.x, object.y, object.z, object.m], + ol.geom.GeometryLayout.XYZM); + } else if (object.z !== undefined) { + point = new ol.geom.Point([object.x, object.y, object.z], + ol.geom.GeometryLayout.XYZ); + } else if (object.m !== undefined) { + point = new ol.geom.Point([object.x, object.y, object.m], + ol.geom.GeometryLayout.XYM); + } else { + point = new ol.geom.Point([object.x, object.y]); + } + return point; +}; + + +/** + * @param {EsriJSONGeometry} object Object. + * @private + * @return {ol.geom.Geometry} LineString. + */ +ol.format.EsriJSON.readLineStringGeometry_ = function(object) { + goog.asserts.assert(Array.isArray(object.paths), + 'object.paths should be an array'); + goog.asserts.assert(object.paths.length === 1, + 'object.paths array length should be 1'); + var layout = ol.format.EsriJSON.getGeometryLayout_(object); + return new ol.geom.LineString(object.paths[0], layout); +}; + + +/** + * @param {EsriJSONGeometry} object Object. + * @private + * @return {ol.geom.Geometry} MultiLineString. + */ +ol.format.EsriJSON.readMultiLineStringGeometry_ = function(object) { + goog.asserts.assert(Array.isArray(object.paths), + 'object.paths should be an array'); + goog.asserts.assert(object.paths.length > 1, + 'object.paths array length should be more than 1'); + var layout = ol.format.EsriJSON.getGeometryLayout_(object); + return new ol.geom.MultiLineString(object.paths, layout); +}; + + +/** + * @param {EsriJSONGeometry} object Object. + * @private + * @return {ol.geom.GeometryLayout} The geometry layout to use. + */ +ol.format.EsriJSON.getGeometryLayout_ = function(object) { + var layout = ol.geom.GeometryLayout.XY; + if (object.hasZ === true && object.hasM === true) { + layout = ol.geom.GeometryLayout.XYZM; + } else if (object.hasZ === true) { + layout = ol.geom.GeometryLayout.XYZ; + } else if (object.hasM === true) { + layout = ol.geom.GeometryLayout.XYM; + } + return layout; +}; + + +/** + * @param {EsriJSONGeometry} object Object. + * @private + * @return {ol.geom.Geometry} MultiPoint. + */ +ol.format.EsriJSON.readMultiPointGeometry_ = function(object) { + goog.asserts.assert(object.points, 'object.points should be defined'); + var layout = ol.format.EsriJSON.getGeometryLayout_(object); + return new ol.geom.MultiPoint(object.points, layout); +}; + + +/** + * @param {EsriJSONGeometry} object Object. + * @private + * @return {ol.geom.Geometry} MultiPolygon. + */ +ol.format.EsriJSON.readMultiPolygonGeometry_ = function(object) { + goog.asserts.assert(object.rings); + goog.asserts.assert(object.rings.length > 1, + 'object.rings should have length larger than 1'); + var layout = ol.format.EsriJSON.getGeometryLayout_(object); + return new ol.geom.MultiPolygon( + /** @type {Array.<Array.<Array.<Array.<number>>>>} */(object.rings), + layout); +}; + + +/** + * @param {EsriJSONGeometry} object Object. + * @private + * @return {ol.geom.Geometry} Polygon. + */ +ol.format.EsriJSON.readPolygonGeometry_ = function(object) { + goog.asserts.assert(object.rings); + var layout = ol.format.EsriJSON.getGeometryLayout_(object); + return new ol.geom.Polygon(object.rings, layout); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {EsriJSONGeometry} EsriJSON geometry. + */ +ol.format.EsriJSON.writePointGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.Point, + 'geometry should be an ol.geom.Point'); + var coordinates = geometry.getCoordinates(); + var layout = geometry.getLayout(); + if (layout === ol.geom.GeometryLayout.XYZ) { + return /** @type {EsriJSONPoint} */ ({ + x: coordinates[0], + y: coordinates[1], + z: coordinates[2] + }); + } else if (layout === ol.geom.GeometryLayout.XYM) { + return /** @type {EsriJSONPoint} */ ({ + x: coordinates[0], + y: coordinates[1], + m: coordinates[2] + }); + } else if (layout === ol.geom.GeometryLayout.XYZM) { + return /** @type {EsriJSONPoint} */ ({ + x: coordinates[0], + y: coordinates[1], + z: coordinates[2], + m: coordinates[3] + }); + } else if (layout === ol.geom.GeometryLayout.XY) { + return /** @type {EsriJSONPoint} */ ({ + x: coordinates[0], + y: coordinates[1] + }); + } else { + goog.asserts.fail('Unknown geometry layout'); + } +}; + + +/** + * @param {ol.geom.SimpleGeometry} geometry Geometry. + * @private + * @return {Object} Object with boolean hasZ and hasM keys. + */ +ol.format.EsriJSON.getHasZM_ = function(geometry) { + var layout = geometry.getLayout(); + return { + hasZ: (layout === ol.geom.GeometryLayout.XYZ || + layout === ol.geom.GeometryLayout.XYZM), + hasM: (layout === ol.geom.GeometryLayout.XYM || + layout === ol.geom.GeometryLayout.XYZM) + }; +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {EsriJSONPolyline} EsriJSON geometry. + */ +ol.format.EsriJSON.writeLineStringGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.LineString, + 'geometry should be an ol.geom.LineString'); + var hasZM = ol.format.EsriJSON.getHasZM_(geometry); + return /** @type {EsriJSONPolyline} */ ({ + hasZ: hasZM.hasZ, + hasM: hasZM.hasM, + paths: [geometry.getCoordinates()] + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {EsriJSONPolygon} EsriJSON geometry. + */ +ol.format.EsriJSON.writePolygonGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.Polygon, + 'geometry should be an ol.geom.Polygon'); + // Esri geometries use the left-hand rule + var hasZM = ol.format.EsriJSON.getHasZM_(geometry); + return /** @type {EsriJSONPolygon} */ ({ + hasZ: hasZM.hasZ, + hasM: hasZM.hasM, + rings: geometry.getCoordinates(false) + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {EsriJSONPolyline} EsriJSON geometry. + */ +ol.format.EsriJSON.writeMultiLineStringGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString, + 'geometry should be an ol.geom.MultiLineString'); + var hasZM = ol.format.EsriJSON.getHasZM_(geometry); + return /** @type {EsriJSONPolyline} */ ({ + hasZ: hasZM.hasZ, + hasM: hasZM.hasM, + paths: geometry.getCoordinates() + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {EsriJSONMultipoint} EsriJSON geometry. + */ +ol.format.EsriJSON.writeMultiPointGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.MultiPoint, + 'geometry should be an ol.geom.MultiPoint'); + var hasZM = ol.format.EsriJSON.getHasZM_(geometry); + return /** @type {EsriJSONMultipoint} */ ({ + hasZ: hasZM.hasZ, + hasM: hasZM.hasM, + points: geometry.getCoordinates() + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {EsriJSONPolygon} EsriJSON geometry. + */ +ol.format.EsriJSON.writeMultiPolygonGeometry_ = function(geometry, + opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.MultiPolygon, + 'geometry should be an ol.geom.MultiPolygon'); + var hasZM = ol.format.EsriJSON.getHasZM_(geometry); + var coordinates = geometry.getCoordinates(false); + var output = []; + for (var i = 0; i < coordinates.length; i++) { + for (var x = coordinates[i].length - 1; x >= 0; x--) { + output.push(coordinates[i][x]); + } + } + return /** @type {EsriJSONPolygon} */ ({ + hasZ: hasZM.hasZ, + hasM: hasZM.hasM, + rings: output + }); +}; + + +/** + * @const + * @private + * @type {Object.<ol.geom.GeometryType, function(EsriJSONGeometry): ol.geom.Geometry>} + */ +ol.format.EsriJSON.GEOMETRY_READERS_ = {}; +ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.POINT] = + ol.format.EsriJSON.readPointGeometry_; +ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.LINE_STRING] = + ol.format.EsriJSON.readLineStringGeometry_; +ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.POLYGON] = + ol.format.EsriJSON.readPolygonGeometry_; +ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_POINT] = + ol.format.EsriJSON.readMultiPointGeometry_; +ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_LINE_STRING] = + ol.format.EsriJSON.readMultiLineStringGeometry_; +ol.format.EsriJSON.GEOMETRY_READERS_[ol.geom.GeometryType.MULTI_POLYGON] = + ol.format.EsriJSON.readMultiPolygonGeometry_; + + +/** + * @const + * @private + * @type {Object.<string, function(ol.geom.Geometry, olx.format.WriteOptions=): (EsriJSONGeometry)>} + */ +ol.format.EsriJSON.GEOMETRY_WRITERS_ = {}; +ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.POINT] = + ol.format.EsriJSON.writePointGeometry_; +ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.LINE_STRING] = + ol.format.EsriJSON.writeLineStringGeometry_; +ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.POLYGON] = + ol.format.EsriJSON.writePolygonGeometry_; +ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_POINT] = + ol.format.EsriJSON.writeMultiPointGeometry_; +ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_LINE_STRING] = + ol.format.EsriJSON.writeMultiLineStringGeometry_; +ol.format.EsriJSON.GEOMETRY_WRITERS_[ol.geom.GeometryType.MULTI_POLYGON] = + ol.format.EsriJSON.writeMultiPolygonGeometry_; + + +/** + * Read a feature from a EsriJSON Feature source. Only works for Feature, + * use `readFeatures` to read FeatureCollection source. + * + * @function + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @api + */ +ol.format.EsriJSON.prototype.readFeature; + + +/** + * Read all features from a EsriJSON source. Works with both Feature and + * FeatureCollection sources. + * + * @function + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api + */ +ol.format.EsriJSON.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.EsriJSON.prototype.readFeatureFromObject = function( + object, opt_options) { + var esriJSONFeature = /** @type {EsriJSONFeature} */ (object); + goog.asserts.assert(esriJSONFeature.geometry || + esriJSONFeature.attributes, + 'geometry or attributes should be defined'); + var geometry = ol.format.EsriJSON.readGeometry_(esriJSONFeature.geometry, + opt_options); + var feature = new ol.Feature(); + if (this.geometryName_) { + feature.setGeometryName(this.geometryName_); + } + feature.setGeometry(geometry); + if (opt_options && opt_options.idField && + esriJSONFeature.attributes[opt_options.idField]) { + goog.asserts.assert( + goog.isNumber(esriJSONFeature.attributes[opt_options.idField]), + 'objectIdFieldName value should be a number'); + feature.setId(/** @type {number} */( + esriJSONFeature.attributes[opt_options.idField])); + } + if (esriJSONFeature.attributes) { + feature.setProperties(esriJSONFeature.attributes); + } + return feature; +}; + + +/** + * @inheritDoc + */ +ol.format.EsriJSON.prototype.readFeaturesFromObject = function( + object, opt_options) { + var esriJSONObject = /** @type {EsriJSONObject} */ (object); + var options = opt_options ? opt_options : {}; + if (esriJSONObject.features) { + var esriJSONFeatureCollection = /** @type {EsriJSONFeatureCollection} */ + (object); + /** @type {Array.<ol.Feature>} */ + var features = []; + var esriJSONFeatures = esriJSONFeatureCollection.features; + var i, ii; + options.idField = object.objectIdFieldName; + for (i = 0, ii = esriJSONFeatures.length; i < ii; ++i) { + features.push(this.readFeatureFromObject(esriJSONFeatures[i], + options)); + } + return features; + } else { + return [this.readFeatureFromObject(object, options)]; + } +}; + + +/** + * Read a geometry from a EsriJSON source. + * + * @function + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.geom.Geometry} Geometry. + * @api + */ +ol.format.EsriJSON.prototype.readGeometry; + + +/** + * @inheritDoc + */ +ol.format.EsriJSON.prototype.readGeometryFromObject = function( + object, opt_options) { + return ol.format.EsriJSON.readGeometry_( + /** @type {EsriJSONGeometry} */ (object), opt_options); +}; + + +/** + * Read the projection from a EsriJSON source. + * + * @function + * @param {ArrayBuffer|Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + * @api + */ +ol.format.EsriJSON.prototype.readProjection; + + +/** + * @inheritDoc + */ +ol.format.EsriJSON.prototype.readProjectionFromObject = function(object) { + var esriJSONObject = /** @type {EsriJSONObject} */ (object); + if (esriJSONObject.spatialReference && esriJSONObject.spatialReference.wkid) { + var crs = esriJSONObject.spatialReference.wkid; + return ol.proj.get('EPSG:' + crs); + } else { + return null; + } +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {EsriJSONGeometry} EsriJSON geometry. + */ +ol.format.EsriJSON.writeGeometry_ = function(geometry, opt_options) { + var geometryWriter = ol.format.EsriJSON.GEOMETRY_WRITERS_[geometry.getType()]; + goog.asserts.assert(geometryWriter, 'geometryWriter should be defined'); + return geometryWriter(/** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions(geometry, true, opt_options)), + opt_options); +}; + + +/** + * Encode a geometry as a EsriJSON string. + * + * @function + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} EsriJSON. + * @api + */ +ol.format.EsriJSON.prototype.writeGeometry; + + +/** + * Encode a geometry as a EsriJSON object. + * + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {EsriJSONGeometry} Object. + * @api + */ +ol.format.EsriJSON.prototype.writeGeometryObject = function(geometry, + opt_options) { + return ol.format.EsriJSON.writeGeometry_(geometry, + this.adaptOptions(opt_options)); +}; + + +/** + * Encode a feature as a EsriJSON Feature string. + * + * @function + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} EsriJSON. + * @api + */ +ol.format.EsriJSON.prototype.writeFeature; + + +/** + * Encode a feature as a esriJSON Feature object. + * + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {Object} Object. + * @api + */ +ol.format.EsriJSON.prototype.writeFeatureObject = function( + feature, opt_options) { + opt_options = this.adaptOptions(opt_options); + var object = {}; + var geometry = feature.getGeometry(); + if (geometry) { + object['geometry'] = + ol.format.EsriJSON.writeGeometry_(geometry, opt_options); + } + var properties = feature.getProperties(); + delete properties[feature.getGeometryName()]; + if (!ol.object.isEmpty(properties)) { + object['attributes'] = properties; + } else { + object['attributes'] = {}; + } + if (opt_options && opt_options.featureProjection) { + object['spatialReference'] = /** @type {EsriJSONCRS} */({ + wkid: ol.proj.get( + opt_options.featureProjection).getCode().split(':').pop() + }); + } + return object; +}; + + +/** + * Encode an array of features as EsriJSON. + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} EsriJSON. + * @api + */ +ol.format.EsriJSON.prototype.writeFeatures; + + +/** + * Encode an array of features as a EsriJSON object. + * + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {Object} EsriJSON Object. + * @api + */ +ol.format.EsriJSON.prototype.writeFeaturesObject = function(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + var objects = []; + var i, ii; + for (i = 0, ii = features.length; i < ii; ++i) { + objects.push(this.writeFeatureObject(features[i], opt_options)); + } + return /** @type {EsriJSONFeatureCollection} */ ({ + 'features': objects + }); +}; + +goog.provide('ol.geom.GeometryCollection'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.object'); + + +/** + * @classdesc + * An array of {@link ol.geom.Geometry} objects. + * + * @constructor + * @extends {ol.geom.Geometry} + * @param {Array.<ol.geom.Geometry>=} opt_geometries Geometries. + * @api stable + */ +ol.geom.GeometryCollection = function(opt_geometries) { + + ol.geom.Geometry.call(this); + + /** + * @private + * @type {Array.<ol.geom.Geometry>} + */ + this.geometries_ = opt_geometries ? opt_geometries : null; + + this.listenGeometriesChange_(); +}; +ol.inherits(ol.geom.GeometryCollection, ol.geom.Geometry); + + +/** + * @param {Array.<ol.geom.Geometry>} geometries Geometries. + * @private + * @return {Array.<ol.geom.Geometry>} Cloned geometries. + */ +ol.geom.GeometryCollection.cloneGeometries_ = function(geometries) { + var clonedGeometries = []; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + clonedGeometries.push(geometries[i].clone()); + } + return clonedGeometries; +}; + + +/** + * @private + */ +ol.geom.GeometryCollection.prototype.unlistenGeometriesChange_ = function() { + var i, ii; + if (!this.geometries_) { + return; + } + for (i = 0, ii = this.geometries_.length; i < ii; ++i) { + ol.events.unlisten( + this.geometries_[i], ol.events.EventType.CHANGE, + this.changed, this); + } +}; + + +/** + * @private + */ +ol.geom.GeometryCollection.prototype.listenGeometriesChange_ = function() { + var i, ii; + if (!this.geometries_) { + return; + } + for (i = 0, ii = this.geometries_.length; i < ii; ++i) { + ol.events.listen( + this.geometries_[i], ol.events.EventType.CHANGE, + this.changed, this); + } +}; + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.GeometryCollection} Clone. + * @api stable + */ +ol.geom.GeometryCollection.prototype.clone = function() { + var geometryCollection = new ol.geom.GeometryCollection(null); + geometryCollection.setGeometries(this.geometries_); + return geometryCollection; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + if (minSquaredDistance < + ol.extent.closestSquaredDistanceXY(this.getExtent(), x, y)) { + return minSquaredDistance; + } + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + minSquaredDistance = geometries[i].closestPointXY( + x, y, closestPoint, minSquaredDistance); + } + return minSquaredDistance; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.containsXY = function(x, y) { + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + if (geometries[i].containsXY(x, y)) { + return true; + } + } + return false; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.computeExtent = function(extent) { + ol.extent.createOrUpdateEmpty(extent); + var geometries = this.geometries_; + for (var i = 0, ii = geometries.length; i < ii; ++i) { + ol.extent.extend(extent, geometries[i].getExtent()); + } + return extent; +}; + + +/** + * Return the geometries that make up this geometry collection. + * @return {Array.<ol.geom.Geometry>} Geometries. + * @api stable + */ +ol.geom.GeometryCollection.prototype.getGeometries = function() { + return ol.geom.GeometryCollection.cloneGeometries_(this.geometries_); +}; + + +/** + * @return {Array.<ol.geom.Geometry>} Geometries. + */ +ol.geom.GeometryCollection.prototype.getGeometriesArray = function() { + return this.geometries_; +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.getSimplifiedGeometry = function(squaredTolerance) { + if (this.simplifiedGeometryRevision != this.getRevision()) { + ol.object.clear(this.simplifiedGeometryCache); + this.simplifiedGeometryMaxMinSquaredTolerance = 0; + this.simplifiedGeometryRevision = this.getRevision(); + } + if (squaredTolerance < 0 || + (this.simplifiedGeometryMaxMinSquaredTolerance !== 0 && + squaredTolerance < this.simplifiedGeometryMaxMinSquaredTolerance)) { + return this; + } + var key = squaredTolerance.toString(); + if (this.simplifiedGeometryCache.hasOwnProperty(key)) { + return this.simplifiedGeometryCache[key]; + } else { + var simplifiedGeometries = []; + var geometries = this.geometries_; + var simplified = false; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + var geometry = geometries[i]; + var simplifiedGeometry = geometry.getSimplifiedGeometry(squaredTolerance); + simplifiedGeometries.push(simplifiedGeometry); + if (simplifiedGeometry !== geometry) { + simplified = true; + } + } + if (simplified) { + var simplifiedGeometryCollection = new ol.geom.GeometryCollection(null); + simplifiedGeometryCollection.setGeometriesArray(simplifiedGeometries); + this.simplifiedGeometryCache[key] = simplifiedGeometryCollection; + return simplifiedGeometryCollection; + } else { + this.simplifiedGeometryMaxMinSquaredTolerance = squaredTolerance; + return this; + } + } +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.GeometryCollection.prototype.getType = function() { + return ol.geom.GeometryType.GEOMETRY_COLLECTION; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.GeometryCollection.prototype.intersectsExtent = function(extent) { + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + if (geometries[i].intersectsExtent(extent)) { + return true; + } + } + return false; +}; + + +/** + * @return {boolean} Is empty. + */ +ol.geom.GeometryCollection.prototype.isEmpty = function() { + return this.geometries_.length === 0; +}; + + +/** + * @inheritDoc + * @api + */ +ol.geom.GeometryCollection.prototype.rotate = function(angle, anchor) { + var geometries = this.geometries_; + for (var i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].rotate(angle, anchor); + } + this.changed(); +}; + + +/** + * Set the geometries that make up this geometry collection. + * @param {Array.<ol.geom.Geometry>} geometries Geometries. + * @api stable + */ +ol.geom.GeometryCollection.prototype.setGeometries = function(geometries) { + this.setGeometriesArray( + ol.geom.GeometryCollection.cloneGeometries_(geometries)); +}; + + +/** + * @param {Array.<ol.geom.Geometry>} geometries Geometries. + */ +ol.geom.GeometryCollection.prototype.setGeometriesArray = function(geometries) { + this.unlistenGeometriesChange_(); + this.geometries_ = geometries; + this.listenGeometriesChange_(); + this.changed(); +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.GeometryCollection.prototype.applyTransform = function(transformFn) { + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].applyTransform(transformFn); + } + this.changed(); +}; + + +/** + * Translate the geometry. + * @param {number} deltaX Delta X. + * @param {number} deltaY Delta Y. + * @api + */ +ol.geom.GeometryCollection.prototype.translate = function(deltaX, deltaY) { + var geometries = this.geometries_; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + geometries[i].translate(deltaX, deltaY); + } + this.changed(); +}; + + +/** + * @inheritDoc + */ +ol.geom.GeometryCollection.prototype.disposeInternal = function() { + this.unlistenGeometriesChange_(); + ol.geom.Geometry.prototype.disposeInternal.call(this); +}; + +// TODO: serialize dataProjection as crs member when writing +// see https://github.com/openlayers/ol3/issues/2078 + +goog.provide('ol.format.GeoJSON'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.format.Feature'); +goog.require('ol.format.JSONFeature'); +goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.object'); +goog.require('ol.proj'); + + +/** + * @classdesc + * Feature format for reading and writing data in the GeoJSON format. + * + * @constructor + * @extends {ol.format.JSONFeature} + * @param {olx.format.GeoJSONOptions=} opt_options Options. + * @api stable + */ +ol.format.GeoJSON = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.JSONFeature.call(this); + + /** + * @inheritDoc + */ + this.defaultDataProjection = ol.proj.get( + options.defaultDataProjection ? + options.defaultDataProjection : 'EPSG:4326'); + + + /** + * Name of the geometry attribute for features. + * @type {string|undefined} + * @private + */ + this.geometryName_ = options.geometryName; + +}; +ol.inherits(ol.format.GeoJSON, ol.format.JSONFeature); + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.GeoJSON.EXTENSIONS_ = ['.geojson']; + + +/** + * @param {GeoJSONGeometry|GeoJSONGeometryCollection} object Object. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @private + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.GeoJSON.readGeometry_ = function(object, opt_options) { + if (!object) { + return null; + } + var geometryReader = ol.format.GeoJSON.GEOMETRY_READERS_[object.type]; + goog.asserts.assert(geometryReader, 'geometryReader should be defined'); + return /** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions( + geometryReader(object), false, opt_options)); +}; + + +/** + * @param {GeoJSONGeometryCollection} object Object. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @private + * @return {ol.geom.GeometryCollection} Geometry collection. + */ +ol.format.GeoJSON.readGeometryCollectionGeometry_ = function( + object, opt_options) { + goog.asserts.assert(object.type == 'GeometryCollection', + 'object.type should be GeometryCollection'); + var geometries = object.geometries.map( + /** + * @param {GeoJSONGeometry} geometry Geometry. + * @return {ol.geom.Geometry} geometry Geometry. + */ + function(geometry) { + return ol.format.GeoJSON.readGeometry_(geometry, opt_options); + }); + return new ol.geom.GeometryCollection(geometries); +}; + + +/** + * @param {GeoJSONGeometry} object Object. + * @private + * @return {ol.geom.Point} Point. + */ +ol.format.GeoJSON.readPointGeometry_ = function(object) { + goog.asserts.assert(object.type == 'Point', + 'object.type should be Point'); + return new ol.geom.Point(object.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} object Object. + * @private + * @return {ol.geom.LineString} LineString. + */ +ol.format.GeoJSON.readLineStringGeometry_ = function(object) { + goog.asserts.assert(object.type == 'LineString', + 'object.type should be LineString'); + return new ol.geom.LineString(object.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} object Object. + * @private + * @return {ol.geom.MultiLineString} MultiLineString. + */ +ol.format.GeoJSON.readMultiLineStringGeometry_ = function(object) { + goog.asserts.assert(object.type == 'MultiLineString', + 'object.type should be MultiLineString'); + return new ol.geom.MultiLineString(object.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} object Object. + * @private + * @return {ol.geom.MultiPoint} MultiPoint. + */ +ol.format.GeoJSON.readMultiPointGeometry_ = function(object) { + goog.asserts.assert(object.type == 'MultiPoint', + 'object.type should be MultiPoint'); + return new ol.geom.MultiPoint(object.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} object Object. + * @private + * @return {ol.geom.MultiPolygon} MultiPolygon. + */ +ol.format.GeoJSON.readMultiPolygonGeometry_ = function(object) { + goog.asserts.assert(object.type == 'MultiPolygon', + 'object.type should be MultiPolygon'); + return new ol.geom.MultiPolygon(object.coordinates); +}; + + +/** + * @param {GeoJSONGeometry} object Object. + * @private + * @return {ol.geom.Polygon} Polygon. + */ +ol.format.GeoJSON.readPolygonGeometry_ = function(object) { + goog.asserts.assert(object.type == 'Polygon', + 'object.type should be Polygon'); + return new ol.geom.Polygon(object.coordinates); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometry|GeoJSONGeometryCollection} GeoJSON geometry. + */ +ol.format.GeoJSON.writeGeometry_ = function(geometry, opt_options) { + var geometryWriter = ol.format.GeoJSON.GEOMETRY_WRITERS_[geometry.getType()]; + goog.asserts.assert(geometryWriter, 'geometryWriter should be defined'); + return geometryWriter(/** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions(geometry, true, opt_options)), + opt_options); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @private + * @return {GeoJSONGeometryCollection} Empty GeoJSON geometry collection. + */ +ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_ = function(geometry) { + return /** @type {GeoJSONGeometryCollection} */ ({ + type: 'GeometryCollection', + geometries: [] + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometryCollection} GeoJSON geometry collection. + */ +ol.format.GeoJSON.writeGeometryCollectionGeometry_ = function( + geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.GeometryCollection, + 'geometry should be an ol.geom.GeometryCollection'); + var geometries = geometry.getGeometriesArray().map(function(geometry) { + var options = ol.object.assign({}, opt_options); + delete options.featureProjection; + return ol.format.GeoJSON.writeGeometry_(geometry, options); + }); + return /** @type {GeoJSONGeometryCollection} */ ({ + type: 'GeometryCollection', + geometries: geometries + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometry} GeoJSON geometry. + */ +ol.format.GeoJSON.writeLineStringGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.LineString, + 'geometry should be an ol.geom.LineString'); + return /** @type {GeoJSONGeometry} */ ({ + type: 'LineString', + coordinates: geometry.getCoordinates() + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometry} GeoJSON geometry. + */ +ol.format.GeoJSON.writeMultiLineStringGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString, + 'geometry should be an ol.geom.MultiLineString'); + return /** @type {GeoJSONGeometry} */ ({ + type: 'MultiLineString', + coordinates: geometry.getCoordinates() + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometry} GeoJSON geometry. + */ +ol.format.GeoJSON.writeMultiPointGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.MultiPoint, + 'geometry should be an ol.geom.MultiPoint'); + return /** @type {GeoJSONGeometry} */ ({ + type: 'MultiPoint', + coordinates: geometry.getCoordinates() + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometry} GeoJSON geometry. + */ +ol.format.GeoJSON.writeMultiPolygonGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.MultiPolygon, + 'geometry should be an ol.geom.MultiPolygon'); + var right; + if (opt_options) { + right = opt_options.rightHanded; + } + return /** @type {GeoJSONGeometry} */ ({ + type: 'MultiPolygon', + coordinates: geometry.getCoordinates(right) + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometry} GeoJSON geometry. + */ +ol.format.GeoJSON.writePointGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.Point, + 'geometry should be an ol.geom.Point'); + return /** @type {GeoJSONGeometry} */ ({ + type: 'Point', + coordinates: geometry.getCoordinates() + }); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @private + * @return {GeoJSONGeometry} GeoJSON geometry. + */ +ol.format.GeoJSON.writePolygonGeometry_ = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.Polygon, + 'geometry should be an ol.geom.Polygon'); + var right; + if (opt_options) { + right = opt_options.rightHanded; + } + return /** @type {GeoJSONGeometry} */ ({ + type: 'Polygon', + coordinates: geometry.getCoordinates(right) + }); +}; + + +/** + * @const + * @private + * @type {Object.<string, function(GeoJSONObject): ol.geom.Geometry>} + */ +ol.format.GeoJSON.GEOMETRY_READERS_ = { + 'Point': ol.format.GeoJSON.readPointGeometry_, + 'LineString': ol.format.GeoJSON.readLineStringGeometry_, + 'Polygon': ol.format.GeoJSON.readPolygonGeometry_, + 'MultiPoint': ol.format.GeoJSON.readMultiPointGeometry_, + 'MultiLineString': ol.format.GeoJSON.readMultiLineStringGeometry_, + 'MultiPolygon': ol.format.GeoJSON.readMultiPolygonGeometry_, + 'GeometryCollection': ol.format.GeoJSON.readGeometryCollectionGeometry_ +}; + + +/** + * @const + * @private + * @type {Object.<string, function(ol.geom.Geometry, olx.format.WriteOptions=): (GeoJSONGeometry|GeoJSONGeometryCollection)>} + */ +ol.format.GeoJSON.GEOMETRY_WRITERS_ = { + 'Point': ol.format.GeoJSON.writePointGeometry_, + 'LineString': ol.format.GeoJSON.writeLineStringGeometry_, + 'Polygon': ol.format.GeoJSON.writePolygonGeometry_, + 'MultiPoint': ol.format.GeoJSON.writeMultiPointGeometry_, + 'MultiLineString': ol.format.GeoJSON.writeMultiLineStringGeometry_, + 'MultiPolygon': ol.format.GeoJSON.writeMultiPolygonGeometry_, + 'GeometryCollection': ol.format.GeoJSON.writeGeometryCollectionGeometry_, + 'Circle': ol.format.GeoJSON.writeEmptyGeometryCollectionGeometry_ +}; + + +/** + * @inheritDoc + */ +ol.format.GeoJSON.prototype.getExtensions = function() { + return ol.format.GeoJSON.EXTENSIONS_; +}; + + +/** + * Read a feature from a GeoJSON Feature source. Only works for Feature, + * use `readFeatures` to read FeatureCollection source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @api stable + */ +ol.format.GeoJSON.prototype.readFeature; + + +/** + * Read all features from a GeoJSON source. Works with both Feature and + * FeatureCollection sources. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.GeoJSON.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.GeoJSON.prototype.readFeatureFromObject = function( + object, opt_options) { + var geoJSONFeature = /** @type {GeoJSONFeature} */ (object); + goog.asserts.assert(geoJSONFeature.type == 'Feature', + 'geoJSONFeature.type should be Feature'); + var geometry = ol.format.GeoJSON.readGeometry_(geoJSONFeature.geometry, + opt_options); + var feature = new ol.Feature(); + if (this.geometryName_) { + feature.setGeometryName(this.geometryName_); + } + feature.setGeometry(geometry); + if (geoJSONFeature.id !== undefined) { + feature.setId(geoJSONFeature.id); + } + if (geoJSONFeature.properties) { + feature.setProperties(geoJSONFeature.properties); + } + return feature; +}; + + +/** + * @inheritDoc + */ +ol.format.GeoJSON.prototype.readFeaturesFromObject = function( + object, opt_options) { + var geoJSONObject = /** @type {GeoJSONObject} */ (object); + if (geoJSONObject.type == 'Feature') { + return [this.readFeatureFromObject(object, opt_options)]; + } else if (geoJSONObject.type == 'FeatureCollection') { + var geoJSONFeatureCollection = /** @type {GeoJSONFeatureCollection} */ + (object); + /** @type {Array.<ol.Feature>} */ + var features = []; + var geoJSONFeatures = geoJSONFeatureCollection.features; + var i, ii; + for (i = 0, ii = geoJSONFeatures.length; i < ii; ++i) { + features.push(this.readFeatureFromObject(geoJSONFeatures[i], + opt_options)); + } + return features; + } else { + goog.asserts.fail('Unknown geoJSONObject.type: ' + geoJSONObject.type); + return []; + } +}; + + +/** + * Read a geometry from a GeoJSON source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.geom.Geometry} Geometry. + * @api stable + */ +ol.format.GeoJSON.prototype.readGeometry; + + +/** + * @inheritDoc + */ +ol.format.GeoJSON.prototype.readGeometryFromObject = function( + object, opt_options) { + return ol.format.GeoJSON.readGeometry_( + /** @type {GeoJSONGeometry} */ (object), opt_options); +}; + + +/** + * Read the projection from a GeoJSON source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + * @api stable + */ +ol.format.GeoJSON.prototype.readProjection; + + +/** + * @inheritDoc + */ +ol.format.GeoJSON.prototype.readProjectionFromObject = function(object) { + var geoJSONObject = /** @type {GeoJSONObject} */ (object); + var crs = geoJSONObject.crs; + if (crs) { + if (crs.type == 'name') { + return ol.proj.get(crs.properties.name); + } else if (crs.type == 'EPSG') { + // 'EPSG' is not part of the GeoJSON specification, but is generated by + // GeoServer. + // TODO: remove this when http://jira.codehaus.org/browse/GEOS-5996 + // is fixed and widely deployed. + return ol.proj.get('EPSG:' + crs.properties.code); + } else { + goog.asserts.fail('Unknown crs.type: ' + crs.type); + return null; + } + } else { + return this.defaultDataProjection; + } +}; + + +/** + * Encode a feature as a GeoJSON Feature string. + * + * @function + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} GeoJSON. + * @api stable + */ +ol.format.GeoJSON.prototype.writeFeature; + + +/** + * Encode a feature as a GeoJSON Feature object. + * + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {GeoJSONFeature} Object. + * @api stable + */ +ol.format.GeoJSON.prototype.writeFeatureObject = function(feature, opt_options) { + opt_options = this.adaptOptions(opt_options); + + var object = /** @type {GeoJSONFeature} */ ({ + 'type': 'Feature' + }); + var id = feature.getId(); + if (id !== undefined) { + object.id = id; + } + var geometry = feature.getGeometry(); + if (geometry) { + object.geometry = + ol.format.GeoJSON.writeGeometry_(geometry, opt_options); + } else { + object.geometry = null; + } + var properties = feature.getProperties(); + delete properties[feature.getGeometryName()]; + if (!ol.object.isEmpty(properties)) { + object.properties = properties; + } else { + object.properties = null; + } + return object; +}; + + +/** + * Encode an array of features as GeoJSON. + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} GeoJSON. + * @api stable + */ +ol.format.GeoJSON.prototype.writeFeatures; + + +/** + * Encode an array of features as a GeoJSON object. + * + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {GeoJSONFeatureCollection} GeoJSON Object. + * @api stable + */ +ol.format.GeoJSON.prototype.writeFeaturesObject = function(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + var objects = []; + var i, ii; + for (i = 0, ii = features.length; i < ii; ++i) { + objects.push(this.writeFeatureObject(features[i], opt_options)); + } + return /** @type {GeoJSONFeatureCollection} */ ({ + type: 'FeatureCollection', + features: objects + }); +}; + + +/** + * Encode a geometry as a GeoJSON string. + * + * @function + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} GeoJSON. + * @api stable + */ +ol.format.GeoJSON.prototype.writeGeometry; + + +/** + * Encode a geometry as a GeoJSON object. + * + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {GeoJSONGeometry|GeoJSONGeometryCollection} Object. + * @api stable + */ +ol.format.GeoJSON.prototype.writeGeometryObject = function(geometry, + opt_options) { + return ol.format.GeoJSON.writeGeometry_(geometry, + this.adaptOptions(opt_options)); +}; + +goog.provide('ol.format.XMLFeature'); + +goog.require('goog.asserts'); +goog.require('ol.array'); +goog.require('ol.format.Feature'); +goog.require('ol.format.FormatType'); +goog.require('ol.proj'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for XML feature formats. + * + * @constructor + * @extends {ol.format.Feature} + */ +ol.format.XMLFeature = function() { + + /** + * @type {XMLSerializer} + * @private + */ + this.xmlSerializer_ = new XMLSerializer(); + + ol.format.Feature.call(this); +}; +ol.inherits(ol.format.XMLFeature, ol.format.Feature); + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.getType = function() { + return ol.format.FormatType.XML; +}; + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.readFeature = function(source, opt_options) { + if (ol.xml.isDocument(source)) { + return this.readFeatureFromDocument( + /** @type {Document} */ (source), opt_options); + } else if (ol.xml.isNode(source)) { + return this.readFeatureFromNode(/** @type {Node} */ (source), opt_options); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readFeatureFromDocument(doc, opt_options); + } else { + goog.asserts.fail('Unknown source type'); + return null; + } +}; + + +/** + * @param {Document} doc Document. + * @param {olx.format.ReadOptions=} opt_options Options. + * @return {ol.Feature} Feature. + */ +ol.format.XMLFeature.prototype.readFeatureFromDocument = function( + doc, opt_options) { + var features = this.readFeaturesFromDocument(doc, opt_options); + if (features.length > 0) { + return features[0]; + } else { + return null; + } +}; + + +/** + * @param {Node} node Node. + * @param {olx.format.ReadOptions=} opt_options Options. + * @return {ol.Feature} Feature. + */ +ol.format.XMLFeature.prototype.readFeatureFromNode = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.readFeatures = function(source, opt_options) { + if (ol.xml.isDocument(source)) { + return this.readFeaturesFromDocument( + /** @type {Document} */ (source), opt_options); + } else if (ol.xml.isNode(source)) { + return this.readFeaturesFromNode(/** @type {Node} */ (source), opt_options); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readFeaturesFromDocument(doc, opt_options); + } else { + goog.asserts.fail('Unknown source type'); + return []; + } +}; + + +/** + * @param {Document} doc Document. + * @param {olx.format.ReadOptions=} opt_options Options. + * @protected + * @return {Array.<ol.Feature>} Features. + */ +ol.format.XMLFeature.prototype.readFeaturesFromDocument = function( + doc, opt_options) { + /** @type {Array.<ol.Feature>} */ + var features = []; + var n; + for (n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + ol.array.extend(features, this.readFeaturesFromNode(n, opt_options)); + } + } + return features; +}; + + +/** + * @param {Node} node Node. + * @param {olx.format.ReadOptions=} opt_options Options. + * @protected + * @return {Array.<ol.Feature>} Features. + */ +ol.format.XMLFeature.prototype.readFeaturesFromNode = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.readGeometry = function(source, opt_options) { + if (ol.xml.isDocument(source)) { + return this.readGeometryFromDocument( + /** @type {Document} */ (source), opt_options); + } else if (ol.xml.isNode(source)) { + return this.readGeometryFromNode(/** @type {Node} */ (source), opt_options); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readGeometryFromDocument(doc, opt_options); + } else { + goog.asserts.fail('Unknown source type'); + return null; + } +}; + + +/** + * @param {Document} doc Document. + * @param {olx.format.ReadOptions=} opt_options Options. + * @protected + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.XMLFeature.prototype.readGeometryFromDocument = goog.abstractMethod; + + +/** + * @param {Node} node Node. + * @param {olx.format.ReadOptions=} opt_options Options. + * @protected + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.XMLFeature.prototype.readGeometryFromNode = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.readProjection = function(source) { + if (ol.xml.isDocument(source)) { + return this.readProjectionFromDocument(/** @type {Document} */ (source)); + } else if (ol.xml.isNode(source)) { + return this.readProjectionFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readProjectionFromDocument(doc); + } else { + goog.asserts.fail('Unknown source type'); + return null; + } +}; + + +/** + * @param {Document} doc Document. + * @protected + * @return {ol.proj.Projection} Projection. + */ +ol.format.XMLFeature.prototype.readProjectionFromDocument = function(doc) { + return this.defaultDataProjection; +}; + + +/** + * @param {Node} node Node. + * @protected + * @return {ol.proj.Projection} Projection. + */ +ol.format.XMLFeature.prototype.readProjectionFromNode = function(node) { + return this.defaultDataProjection; +}; + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.writeFeature = function(feature, opt_options) { + var node = this.writeFeatureNode(feature, opt_options); + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + return this.xmlSerializer_.serializeToString(node); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Options. + * @protected + * @return {Node} Node. + */ +ol.format.XMLFeature.prototype.writeFeatureNode = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.writeFeatures = function(features, opt_options) { + var node = this.writeFeaturesNode(features, opt_options); + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + return this.xmlSerializer_.serializeToString(node); +}; + + +/** + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {Node} Node. + */ +ol.format.XMLFeature.prototype.writeFeaturesNode = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.XMLFeature.prototype.writeGeometry = function(geometry, opt_options) { + var node = this.writeGeometryNode(geometry, opt_options); + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + return this.xmlSerializer_.serializeToString(node); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {Node} Node. + */ +ol.format.XMLFeature.prototype.writeGeometryNode = goog.abstractMethod; + +// FIXME Envelopes should not be treated as geometries! readEnvelope_ is part +// of GEOMETRY_PARSERS_ and methods using GEOMETRY_PARSERS_ do not expect +// envelopes/extents, only geometries! +goog.provide('ol.format.GMLBase'); + +goog.require('goog.asserts'); +goog.require('ol.array'); +goog.require('ol.Feature'); +goog.require('ol.format.Feature'); +goog.require('ol.format.XMLFeature'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Feature base format for reading and writing data in the GML format. + * This class cannot be instantiated, it contains only base content that + * is shared with versioned format classes ol.format.GML2 and + * ol.format.GML3. + * + * @constructor + * @param {olx.format.GMLOptions=} opt_options + * Optional configuration object. + * @extends {ol.format.XMLFeature} + */ +ol.format.GMLBase = function(opt_options) { + var options = /** @type {olx.format.GMLOptions} */ + (opt_options ? opt_options : {}); + + /** + * @protected + * @type {Array.<string>|string|undefined} + */ + this.featureType = options.featureType; + + /** + * @protected + * @type {Object.<string, string>|string|undefined} + */ + this.featureNS = options.featureNS; + + /** + * @protected + * @type {string} + */ + this.srsName = options.srsName; + + /** + * @protected + * @type {string} + */ + this.schemaLocation = ''; + + /** + * @type {Object.<string, Object.<string, Object>>} + */ + this.FEATURE_COLLECTION_PARSERS = {}; + this.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS] = { + 'featureMember': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readFeaturesInternal), + 'featureMembers': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readFeaturesInternal) + }; + + ol.format.XMLFeature.call(this); +}; +ol.inherits(ol.format.GMLBase, ol.format.XMLFeature); + + +/** + * @const + * @type {string} + */ +ol.format.GMLBase.GMLNS = 'http://www.opengis.net/gml'; + + +/** + * A regular expression that matches if a string only contains whitespace + * characters. It will e.g. match `''`, `' '`, `'\n'` etc. The non-breaking + * space (0xa0) is explicitly included as IE doesn't include it in its + * definition of `\s`. + * + * Information from `goog.string.isEmptyOrWhitespace`: https://github.com/google/closure-library/blob/e877b1e/closure/goog/string/string.js#L156-L160 + * + * @const + * @type {RegExp} + * @private + */ +ol.format.GMLBase.ONLY_WHITESPACE_RE_ = /^[\s\xa0]*$/; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Array.<ol.Feature> | undefined} Features. + */ +ol.format.GMLBase.prototype.readFeaturesInternal = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + var localName = node.localName; + var features = null; + if (localName == 'FeatureCollection') { + if (node.namespaceURI === 'http://www.opengis.net/wfs') { + features = ol.xml.pushParseAndPop([], + this.FEATURE_COLLECTION_PARSERS, node, + objectStack, this); + } else { + features = ol.xml.pushParseAndPop(null, + this.FEATURE_COLLECTION_PARSERS, node, + objectStack, this); + } + } else if (localName == 'featureMembers' || localName == 'featureMember') { + var context = objectStack[0]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var featureType = context['featureType']; + var featureNS = context['featureNS']; + var i, ii, prefix = 'p', defaultPrefix = 'p0'; + if (!featureType && node.childNodes) { + featureType = [], featureNS = {}; + for (i = 0, ii = node.childNodes.length; i < ii; ++i) { + var child = node.childNodes[i]; + if (child.nodeType === 1) { + var ft = child.nodeName.split(':').pop(); + if (featureType.indexOf(ft) === -1) { + var key = ''; + var count = 0; + var uri = child.namespaceURI; + for (var candidate in featureNS) { + if (featureNS[candidate] === uri) { + key = candidate; + break; + } + ++count; + } + if (!key) { + key = prefix + count; + featureNS[key] = uri; + } + featureType.push(key + ':' + ft); + } + } + } + if (localName != 'featureMember') { + // recheck featureType for each featureMember + context['featureType'] = featureType; + context['featureNS'] = featureNS; + } + } + if (typeof featureNS === 'string') { + var ns = featureNS; + featureNS = {}; + featureNS[defaultPrefix] = ns; + } + var parsersNS = {}; + var featureTypes = Array.isArray(featureType) ? featureType : [featureType]; + for (var p in featureNS) { + var parsers = {}; + for (i = 0, ii = featureTypes.length; i < ii; ++i) { + var featurePrefix = featureTypes[i].indexOf(':') === -1 ? + defaultPrefix : featureTypes[i].split(':')[0]; + if (featurePrefix === p) { + parsers[featureTypes[i].split(':').pop()] = + (localName == 'featureMembers') ? + ol.xml.makeArrayPusher(this.readFeatureElement, this) : + ol.xml.makeReplacer(this.readFeatureElement, this); + } + } + parsersNS[featureNS[p]] = parsers; + } + if (localName == 'featureMember') { + features = ol.xml.pushParseAndPop(undefined, parsersNS, node, objectStack); + } else { + features = ol.xml.pushParseAndPop([], parsersNS, node, objectStack); + } + } + if (features === null) { + features = []; + } + return features; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.Geometry|undefined} Geometry. + */ +ol.format.GMLBase.prototype.readGeometryElement = function(node, objectStack) { + var context = objectStack[0]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + context['srsName'] = node.firstElementChild.getAttribute('srsName'); + /** @type {ol.geom.Geometry} */ + var geometry = ol.xml.pushParseAndPop(null, + this.GEOMETRY_PARSERS_, node, objectStack, this); + if (geometry) { + return /** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions(geometry, false, context)); + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.Feature} Feature. + */ +ol.format.GMLBase.prototype.readFeatureElement = function(node, objectStack) { + var n; + var fid = node.getAttribute('fid') || + ol.xml.getAttributeNS(node, ol.format.GMLBase.GMLNS, 'id'); + var values = {}, geometryName; + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + var localName = n.localName; + // Assume attribute elements have one child node and that the child + // is a text or CDATA node (to be treated as text). + // Otherwise assume it is a geometry node. + if (n.childNodes.length === 0 || + (n.childNodes.length === 1 && + (n.firstChild.nodeType === 3 || n.firstChild.nodeType === 4))) { + var value = ol.xml.getAllTextContent(n, false); + if (ol.format.GMLBase.ONLY_WHITESPACE_RE_.test(value)) { + value = undefined; + } + values[localName] = value; + } else { + // boundedBy is an extent and must not be considered as a geometry + if (localName !== 'boundedBy') { + geometryName = localName; + } + values[localName] = this.readGeometryElement(n, objectStack); + } + } + var feature = new ol.Feature(values); + if (geometryName) { + feature.setGeometryName(geometryName); + } + if (fid) { + feature.setId(fid); + } + return feature; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.Point|undefined} Point. + */ +ol.format.GMLBase.prototype.readPoint = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Point', 'localName should be Point'); + var flatCoordinates = + this.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + var point = new ol.geom.Point(null); + goog.asserts.assert(flatCoordinates.length == 3, + 'flatCoordinates should have a length of 3'); + point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + return point; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.MultiPoint|undefined} MultiPoint. + */ +ol.format.GMLBase.prototype.readMultiPoint = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'MultiPoint', + 'localName should be MultiPoint'); + /** @type {Array.<Array.<number>>} */ + var coordinates = ol.xml.pushParseAndPop([], + this.MULTIPOINT_PARSERS_, node, objectStack, this); + if (coordinates) { + return new ol.geom.MultiPoint(coordinates); + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.MultiLineString|undefined} MultiLineString. + */ +ol.format.GMLBase.prototype.readMultiLineString = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'MultiLineString', + 'localName should be MultiLineString'); + /** @type {Array.<ol.geom.LineString>} */ + var lineStrings = ol.xml.pushParseAndPop([], + this.MULTILINESTRING_PARSERS_, node, objectStack, this); + if (lineStrings) { + var multiLineString = new ol.geom.MultiLineString(null); + multiLineString.setLineStrings(lineStrings); + return multiLineString; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.MultiPolygon|undefined} MultiPolygon. + */ +ol.format.GMLBase.prototype.readMultiPolygon = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'MultiPolygon', + 'localName should be MultiPolygon'); + /** @type {Array.<ol.geom.Polygon>} */ + var polygons = ol.xml.pushParseAndPop([], + this.MULTIPOLYGON_PARSERS_, node, objectStack, this); + if (polygons) { + var multiPolygon = new ol.geom.MultiPolygon(null); + multiPolygon.setPolygons(polygons); + return multiPolygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GMLBase.prototype.pointMemberParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'pointMember' || + node.localName == 'pointMembers', + 'localName should be pointMember or pointMembers'); + ol.xml.parseNode(this.POINTMEMBER_PARSERS_, + node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GMLBase.prototype.lineStringMemberParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'lineStringMember' || + node.localName == 'lineStringMembers', + 'localName should be LineStringMember or LineStringMembers'); + ol.xml.parseNode(this.LINESTRINGMEMBER_PARSERS_, + node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GMLBase.prototype.polygonMemberParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'polygonMember' || + node.localName == 'polygonMembers', + 'localName should be polygonMember or polygonMembers'); + ol.xml.parseNode(this.POLYGONMEMBER_PARSERS_, node, + objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.LineString|undefined} LineString. + */ +ol.format.GMLBase.prototype.readLineString = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LineString', + 'localName should be LineString'); + var flatCoordinates = + this.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + return lineString; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>|undefined} LinearRing flat coordinates. + */ +ol.format.GMLBase.prototype.readFlatLinearRing_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LinearRing', + 'localName should be LinearRing'); + var ring = ol.xml.pushParseAndPop(null, + this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, + objectStack, this); + if (ring) { + return ring; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.LinearRing|undefined} LinearRing. + */ +ol.format.GMLBase.prototype.readLinearRing = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LinearRing', + 'localName should be LinearRing'); + var flatCoordinates = + this.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + var ring = new ol.geom.LinearRing(null); + ring.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + return ring; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.geom.Polygon|undefined} Polygon. + */ +ol.format.GMLBase.prototype.readPolygon = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Polygon', + 'localName should be Polygon'); + /** @type {Array.<Array.<number>>} */ + var flatLinearRings = ol.xml.pushParseAndPop([null], + this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); + if (flatLinearRings && flatLinearRings[0]) { + var polygon = new ol.geom.Polygon(null); + var flatCoordinates = flatLinearRings[0]; + var ends = [flatCoordinates.length]; + var i, ii; + for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { + ol.array.extend(flatCoordinates, flatLinearRings[i]); + ends.push(flatCoordinates.length); + } + polygon.setFlatCoordinates( + ol.geom.GeometryLayout.XYZ, flatCoordinates, ends); + return polygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>} Flat coordinates. + */ +ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + return ol.xml.pushParseAndPop(null, + this.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, + objectStack, this); +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GMLBase.prototype.MULTIPOINT_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'pointMember': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.pointMemberParser_), + 'pointMembers': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.pointMemberParser_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GMLBase.prototype.MULTILINESTRING_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'lineStringMember': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.lineStringMemberParser_), + 'lineStringMembers': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.lineStringMemberParser_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GMLBase.prototype.MULTIPOLYGON_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'polygonMember': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.polygonMemberParser_), + 'polygonMembers': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.polygonMemberParser_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GMLBase.prototype.POINTMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'Point': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.readFlatCoordinatesFromNode_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GMLBase.prototype.LINESTRINGMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'LineString': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.readLineString) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GMLBase.prototype.POLYGONMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'Polygon': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.readPolygon) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @protected + */ +ol.format.GMLBase.prototype.RING_PARSERS = { + 'http://www.opengis.net/gml' : { + 'LinearRing': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readFlatLinearRing_) + } +}; + + +/** + * @inheritDoc + */ +ol.format.GMLBase.prototype.readGeometryFromNode = function(node, opt_options) { + var geometry = this.readGeometryElement(node, + [this.getReadOptions(node, opt_options ? opt_options : {})]); + return geometry ? geometry : null; +}; + + +/** + * Read all features from a GML FeatureCollection. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.GMLBase.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.GMLBase.prototype.readFeaturesFromNode = function(node, opt_options) { + var options = { + featureType: this.featureType, + featureNS: this.featureNS + }; + if (opt_options) { + ol.object.assign(options, this.getReadOptions(node, opt_options)); + } + var features = this.readFeaturesInternal(node, [options]); + return features || []; +}; + + +/** + * @inheritDoc + */ +ol.format.GMLBase.prototype.readProjectionFromNode = function(node) { + return ol.proj.get(this.srsName ? this.srsName : + node.firstElementChild.getAttribute('srsName')); +}; + +goog.provide('ol.format.XSD'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.xml'); +goog.require('ol.string'); + + +/** + * @const + * @type {string} + */ +ol.format.XSD.NAMESPACE_URI = 'http://www.w3.org/2001/XMLSchema'; + + +/** + * @param {Node} node Node. + * @return {boolean|undefined} Boolean. + */ +ol.format.XSD.readBoolean = function(node) { + var s = ol.xml.getAllTextContent(node, false); + return ol.format.XSD.readBooleanString(s); +}; + + +/** + * @param {string} string String. + * @return {boolean|undefined} Boolean. + */ +ol.format.XSD.readBooleanString = function(string) { + var m = /^\s*(true|1)|(false|0)\s*$/.exec(string); + if (m) { + return m[1] !== undefined || false; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @return {number|undefined} DateTime in seconds. + */ +ol.format.XSD.readDateTime = function(node) { + var s = ol.xml.getAllTextContent(node, false); + var re = + /^\s*(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?))\s*$/; + var m = re.exec(s); + if (m) { + var year = parseInt(m[1], 10); + var month = parseInt(m[2], 10) - 1; + var day = parseInt(m[3], 10); + var hour = parseInt(m[4], 10); + var minute = parseInt(m[5], 10); + var second = parseInt(m[6], 10); + var dateTime = Date.UTC(year, month, day, hour, minute, second) / 1000; + if (m[7] != 'Z') { + var sign = m[8] == '-' ? -1 : 1; + dateTime += sign * 60 * parseInt(m[9], 10); + if (m[10] !== undefined) { + dateTime += sign * 60 * 60 * parseInt(m[10], 10); + } + } + return dateTime; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @return {number|undefined} Decimal. + */ +ol.format.XSD.readDecimal = function(node) { + var s = ol.xml.getAllTextContent(node, false); + return ol.format.XSD.readDecimalString(s); +}; + + +/** + * @param {string} string String. + * @return {number|undefined} Decimal. + */ +ol.format.XSD.readDecimalString = function(string) { + // FIXME check spec + var m = /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(string); + if (m) { + return parseFloat(m[1]); + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @return {number|undefined} Non negative integer. + */ +ol.format.XSD.readNonNegativeInteger = function(node) { + var s = ol.xml.getAllTextContent(node, false); + return ol.format.XSD.readNonNegativeIntegerString(s); +}; + + +/** + * @param {string} string String. + * @return {number|undefined} Non negative integer. + */ +ol.format.XSD.readNonNegativeIntegerString = function(string) { + var m = /^\s*(\d+)\s*$/.exec(string); + if (m) { + return parseInt(m[1], 10); + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @return {string|undefined} String. + */ +ol.format.XSD.readString = function(node) { + return ol.xml.getAllTextContent(node, false).trim(); +}; + + +/** + * @param {Node} node Node to append a TextNode with the boolean to. + * @param {boolean} bool Boolean. + */ +ol.format.XSD.writeBooleanTextNode = function(node, bool) { + ol.format.XSD.writeStringTextNode(node, (bool) ? '1' : '0'); +}; + + +/** + * @param {Node} node Node to append a TextNode with the dateTime to. + * @param {number} dateTime DateTime in seconds. + */ +ol.format.XSD.writeDateTimeTextNode = function(node, dateTime) { + var date = new Date(dateTime * 1000); + var string = date.getUTCFullYear() + '-' + + ol.string.padNumber(date.getUTCMonth() + 1, 2) + '-' + + ol.string.padNumber(date.getUTCDate(), 2) + 'T' + + ol.string.padNumber(date.getUTCHours(), 2) + ':' + + ol.string.padNumber(date.getUTCMinutes(), 2) + ':' + + ol.string.padNumber(date.getUTCSeconds(), 2) + 'Z'; + node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); +}; + + +/** + * @param {Node} node Node to append a TextNode with the decimal to. + * @param {number} decimal Decimal. + */ +ol.format.XSD.writeDecimalTextNode = function(node, decimal) { + var string = decimal.toPrecision(); + node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); +}; + + +/** + * @param {Node} node Node to append a TextNode with the decimal to. + * @param {number} nonNegativeInteger Non negative integer. + */ +ol.format.XSD.writeNonNegativeIntegerTextNode = function(node, nonNegativeInteger) { + goog.asserts.assert(nonNegativeInteger >= 0, 'value should be more than 0'); + goog.asserts.assert(nonNegativeInteger == (nonNegativeInteger | 0), + 'value should be an integer value'); + var string = nonNegativeInteger.toString(); + node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); +}; + + +/** + * @param {Node} node Node to append a TextNode with the string to. + * @param {string} string String. + */ +ol.format.XSD.writeStringTextNode = function(node, string) { + node.appendChild(ol.xml.DOCUMENT.createTextNode(string)); +}; + +goog.provide('ol.format.GML2'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.format.GMLBase'); +goog.require('ol.format.XSD'); +goog.require('ol.proj'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Feature format for reading and writing data in the GML format, + * version 2.1.2. + * + * @constructor + * @param {olx.format.GMLOptions=} opt_options Optional configuration object. + * @extends {ol.format.GMLBase} + * @api + */ +ol.format.GML2 = function(opt_options) { + var options = /** @type {olx.format.GMLOptions} */ + (opt_options ? opt_options : {}); + + ol.format.GMLBase.call(this, options); + + this.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS][ + 'featureMember'] = + ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readFeaturesInternal); + + /** + * @inheritDoc + */ + this.schemaLocation = options.schemaLocation ? + options.schemaLocation : ol.format.GML2.schemaLocation_; + +}; +ol.inherits(ol.format.GML2, ol.format.GMLBase); + + +/** + * @const + * @type {string} + * @private + */ +ol.format.GML2.schemaLocation_ = ol.format.GMLBase.GMLNS + + ' http://schemas.opengis.net/gml/2.1.2/feature.xsd'; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>|undefined} Flat coordinates. + */ +ol.format.GML2.prototype.readFlatCoordinates_ = function(node, objectStack) { + var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); + var context = /** @type {ol.XmlNodeStackItem} */ (objectStack[0]); + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var containerSrs = context['srsName']; + var containerDimension = node.parentNode.getAttribute('srsDimension'); + var axisOrientation = 'enu'; + if (containerSrs) { + var proj = ol.proj.get(containerSrs); + if (proj) { + axisOrientation = proj.getAxisOrientation(); + } + } + var coords = s.split(/[\s,]+/); + // The "dimension" attribute is from the GML 3.0.1 spec. + var dim = 2; + if (node.getAttribute('srsDimension')) { + dim = ol.format.XSD.readNonNegativeIntegerString( + node.getAttribute('srsDimension')); + } else if (node.getAttribute('dimension')) { + dim = ol.format.XSD.readNonNegativeIntegerString( + node.getAttribute('dimension')); + } else if (containerDimension) { + dim = ol.format.XSD.readNonNegativeIntegerString(containerDimension); + } + var x, y, z; + var flatCoordinates = []; + for (var i = 0, ii = coords.length; i < ii; i += dim) { + x = parseFloat(coords[i]); + y = parseFloat(coords[i + 1]); + z = (dim === 3) ? parseFloat(coords[i + 2]) : 0; + if (axisOrientation.substr(0, 2) === 'en') { + flatCoordinates.push(x, y, z); + } else { + flatCoordinates.push(y, x, z); + } + } + return flatCoordinates; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.Extent|undefined} Envelope. + */ +ol.format.GML2.prototype.readBox_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Box', 'localName should be Box'); + /** @type {Array.<number>} */ + var flatCoordinates = ol.xml.pushParseAndPop([null], + this.BOX_PARSERS_, node, objectStack, this); + return ol.extent.createOrUpdate(flatCoordinates[1][0], + flatCoordinates[1][1], flatCoordinates[1][3], + flatCoordinates[1][4]); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML2.prototype.innerBoundaryIsParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'innerBoundaryIs', + 'localName should be innerBoundaryIs'); + /** @type {Array.<number>|undefined} */ + var flatLinearRing = ol.xml.pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + var flatLinearRings = /** @type {Array.<Array.<number>>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(Array.isArray(flatLinearRings), + 'flatLinearRings should be an array'); + goog.asserts.assert(flatLinearRings.length > 0, + 'flatLinearRings should have an array length larger than 0'); + flatLinearRings.push(flatLinearRing); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML2.prototype.outerBoundaryIsParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'outerBoundaryIs', + 'localName should be outerBoundaryIs'); + /** @type {Array.<number>|undefined} */ + var flatLinearRing = ol.xml.pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + var flatLinearRings = /** @type {Array.<Array.<number>>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(Array.isArray(flatLinearRings), + 'flatLinearRings should be an array'); + goog.asserts.assert(flatLinearRings.length > 0, + 'flatLinearRings should have an array length larger than 0'); + flatLinearRings[0] = flatLinearRing; + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML2.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'coordinates': ol.xml.makeReplacer( + ol.format.GML2.prototype.readFlatCoordinates_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML2.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'innerBoundaryIs': ol.format.GML2.prototype.innerBoundaryIsParser_, + 'outerBoundaryIs': ol.format.GML2.prototype.outerBoundaryIsParser_ + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML2.prototype.BOX_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'coordinates': ol.xml.makeArrayPusher( + ol.format.GML2.prototype.readFlatCoordinates_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML2.prototype.GEOMETRY_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint), + 'MultiPoint': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readMultiPoint), + 'LineString': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readLineString), + 'MultiLineString': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readMultiLineString), + 'LinearRing' : ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readLinearRing), + 'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon), + 'MultiPolygon': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readMultiPolygon), + 'Box': ol.xml.makeReplacer(ol.format.GML2.prototype.readBox_) + } +}; + +goog.provide('ol.format.GML'); +goog.provide('ol.format.GML3'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.array'); +goog.require('ol.Feature'); +goog.require('ol.extent'); +goog.require('ol.format.Feature'); +goog.require('ol.format.GMLBase'); +goog.require('ol.format.XSD'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Feature format for reading and writing data in the GML format + * version 3.1.1. + * Currently only supports GML 3.1.1 Simple Features profile. + * + * @constructor + * @param {olx.format.GMLOptions=} opt_options + * Optional configuration object. + * @extends {ol.format.GMLBase} + * @api + */ +ol.format.GML3 = function(opt_options) { + var options = /** @type {olx.format.GMLOptions} */ + (opt_options ? opt_options : {}); + + ol.format.GMLBase.call(this, options); + + /** + * @private + * @type {boolean} + */ + this.surface_ = options.surface !== undefined ? options.surface : false; + + /** + * @private + * @type {boolean} + */ + this.curve_ = options.curve !== undefined ? options.curve : false; + + /** + * @private + * @type {boolean} + */ + this.multiCurve_ = options.multiCurve !== undefined ? + options.multiCurve : true; + + /** + * @private + * @type {boolean} + */ + this.multiSurface_ = options.multiSurface !== undefined ? + options.multiSurface : true; + + /** + * @inheritDoc + */ + this.schemaLocation = options.schemaLocation ? + options.schemaLocation : ol.format.GML3.schemaLocation_; + +}; +ol.inherits(ol.format.GML3, ol.format.GMLBase); + + +/** + * @const + * @type {string} + * @private + */ +ol.format.GML3.schemaLocation_ = ol.format.GMLBase.GMLNS + + ' http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/' + + '1.0.0/gmlsf.xsd'; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.MultiLineString|undefined} MultiLineString. + */ +ol.format.GML3.prototype.readMultiCurve_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'MultiCurve', + 'localName should be MultiCurve'); + /** @type {Array.<ol.geom.LineString>} */ + var lineStrings = ol.xml.pushParseAndPop([], + this.MULTICURVE_PARSERS_, node, objectStack, this); + if (lineStrings) { + var multiLineString = new ol.geom.MultiLineString(null); + multiLineString.setLineStrings(lineStrings); + return multiLineString; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.MultiPolygon|undefined} MultiPolygon. + */ +ol.format.GML3.prototype.readMultiSurface_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'MultiSurface', + 'localName should be MultiSurface'); + /** @type {Array.<ol.geom.Polygon>} */ + var polygons = ol.xml.pushParseAndPop([], + this.MULTISURFACE_PARSERS_, node, objectStack, this); + if (polygons) { + var multiPolygon = new ol.geom.MultiPolygon(null); + multiPolygon.setPolygons(polygons); + return multiPolygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML3.prototype.curveMemberParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'curveMember' || + node.localName == 'curveMembers', + 'localName should be curveMember or curveMembers'); + ol.xml.parseNode(this.CURVEMEMBER_PARSERS_, node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML3.prototype.surfaceMemberParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'surfaceMember' || + node.localName == 'surfaceMembers', + 'localName should be surfaceMember or surfaceMembers'); + ol.xml.parseNode(this.SURFACEMEMBER_PARSERS_, + node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<(Array.<number>)>|undefined} flat coordinates. + */ +ol.format.GML3.prototype.readPatch_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'patches', + 'localName should be patches'); + return ol.xml.pushParseAndPop([null], + this.PATCHES_PARSERS_, node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>|undefined} flat coordinates. + */ +ol.format.GML3.prototype.readSegment_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'segments', + 'localName should be segments'); + return ol.xml.pushParseAndPop([null], + this.SEGMENTS_PARSERS_, node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<(Array.<number>)>|undefined} flat coordinates. + */ +ol.format.GML3.prototype.readPolygonPatch_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'npde.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'PolygonPatch', + 'localName should be PolygonPatch'); + return ol.xml.pushParseAndPop([null], + this.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>|undefined} flat coordinates. + */ +ol.format.GML3.prototype.readLineStringSegment_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LineStringSegment', + 'localName should be LineStringSegment'); + return ol.xml.pushParseAndPop([null], + this.GEOMETRY_FLAT_COORDINATES_PARSERS_, + node, objectStack, this); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML3.prototype.interiorParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'interior', + 'localName should be interior'); + /** @type {Array.<number>|undefined} */ + var flatLinearRing = ol.xml.pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + var flatLinearRings = /** @type {Array.<Array.<number>>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(Array.isArray(flatLinearRings), + 'flatLinearRings should be an array'); + goog.asserts.assert(flatLinearRings.length > 0, + 'flatLinearRings should have an array length of 1 or more'); + flatLinearRings.push(flatLinearRing); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GML3.prototype.exteriorParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'exterior', + 'localName should be exterior'); + /** @type {Array.<number>|undefined} */ + var flatLinearRing = ol.xml.pushParseAndPop(undefined, + this.RING_PARSERS, node, objectStack, this); + if (flatLinearRing) { + var flatLinearRings = /** @type {Array.<Array.<number>>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(Array.isArray(flatLinearRings), + 'flatLinearRings should be an array'); + goog.asserts.assert(flatLinearRings.length > 0, + 'flatLinearRings should have an array length of 1 or more'); + flatLinearRings[0] = flatLinearRing; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Polygon|undefined} Polygon. + */ +ol.format.GML3.prototype.readSurface_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Surface', + 'localName should be Surface'); + /** @type {Array.<Array.<number>>} */ + var flatLinearRings = ol.xml.pushParseAndPop([null], + this.SURFACE_PARSERS_, node, objectStack, this); + if (flatLinearRings && flatLinearRings[0]) { + var polygon = new ol.geom.Polygon(null); + var flatCoordinates = flatLinearRings[0]; + var ends = [flatCoordinates.length]; + var i, ii; + for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { + ol.array.extend(flatCoordinates, flatLinearRings[i]); + ends.push(flatCoordinates.length); + } + polygon.setFlatCoordinates( + ol.geom.GeometryLayout.XYZ, flatCoordinates, ends); + return polygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.LineString|undefined} LineString. + */ +ol.format.GML3.prototype.readCurve_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Curve', 'localName should be Curve'); + /** @type {Array.<number>} */ + var flatCoordinates = ol.xml.pushParseAndPop([null], + this.CURVE_PARSERS_, node, objectStack, this); + if (flatCoordinates) { + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + return lineString; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.Extent|undefined} Envelope. + */ +ol.format.GML3.prototype.readEnvelope_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Envelope', + 'localName should be Envelope'); + /** @type {Array.<number>} */ + var flatCoordinates = ol.xml.pushParseAndPop([null], + this.ENVELOPE_PARSERS_, node, objectStack, this); + return ol.extent.createOrUpdate(flatCoordinates[1][0], + flatCoordinates[1][1], flatCoordinates[2][0], + flatCoordinates[2][1]); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>|undefined} Flat coordinates. + */ +ol.format.GML3.prototype.readFlatPos_ = function(node, objectStack) { + var s = ol.xml.getAllTextContent(node, false); + var re = /^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/; + /** @type {Array.<number>} */ + var flatCoordinates = []; + var m; + while ((m = re.exec(s))) { + flatCoordinates.push(parseFloat(m[1])); + s = s.substr(m[0].length); + } + if (s !== '') { + return undefined; + } + var context = objectStack[0]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var containerSrs = context['srsName']; + var axisOrientation = 'enu'; + if (containerSrs) { + var proj = ol.proj.get(containerSrs); + axisOrientation = proj.getAxisOrientation(); + } + if (axisOrientation === 'neu') { + var i, ii; + for (i = 0, ii = flatCoordinates.length; i < ii; i += 3) { + var y = flatCoordinates[i]; + var x = flatCoordinates[i + 1]; + flatCoordinates[i] = x; + flatCoordinates[i + 1] = y; + } + } + var len = flatCoordinates.length; + if (len == 2) { + flatCoordinates.push(0); + } + if (len === 0) { + return undefined; + } + return flatCoordinates; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>|undefined} Flat coordinates. + */ +ol.format.GML3.prototype.readFlatPosList_ = function(node, objectStack) { + var s = ol.xml.getAllTextContent(node, false).replace(/^\s*|\s*$/g, ''); + var context = objectStack[0]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var containerSrs = context['srsName']; + var containerDimension = node.parentNode.getAttribute('srsDimension'); + var axisOrientation = 'enu'; + if (containerSrs) { + var proj = ol.proj.get(containerSrs); + axisOrientation = proj.getAxisOrientation(); + } + var coords = s.split(/\s+/); + // The "dimension" attribute is from the GML 3.0.1 spec. + var dim = 2; + if (node.getAttribute('srsDimension')) { + dim = ol.format.XSD.readNonNegativeIntegerString( + node.getAttribute('srsDimension')); + } else if (node.getAttribute('dimension')) { + dim = ol.format.XSD.readNonNegativeIntegerString( + node.getAttribute('dimension')); + } else if (containerDimension) { + dim = ol.format.XSD.readNonNegativeIntegerString(containerDimension); + } + var x, y, z; + var flatCoordinates = []; + for (var i = 0, ii = coords.length; i < ii; i += dim) { + x = parseFloat(coords[i]); + y = parseFloat(coords[i + 1]); + z = (dim === 3) ? parseFloat(coords[i + 2]) : 0; + if (axisOrientation.substr(0, 2) === 'en') { + flatCoordinates.push(x, y, z); + } else { + flatCoordinates.push(y, x, z); + } + } + return flatCoordinates; +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.GEOMETRY_FLAT_COORDINATES_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'pos': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPos_), + 'posList': ol.xml.makeReplacer(ol.format.GML3.prototype.readFlatPosList_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.FLAT_LINEAR_RINGS_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'interior': ol.format.GML3.prototype.interiorParser_, + 'exterior': ol.format.GML3.prototype.exteriorParser_ + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.GEOMETRY_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'Point': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPoint), + 'MultiPoint': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readMultiPoint), + 'LineString': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readLineString), + 'MultiLineString': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readMultiLineString), + 'LinearRing' : ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readLinearRing), + 'Polygon': ol.xml.makeReplacer(ol.format.GMLBase.prototype.readPolygon), + 'MultiPolygon': ol.xml.makeReplacer( + ol.format.GMLBase.prototype.readMultiPolygon), + 'Surface': ol.xml.makeReplacer(ol.format.GML3.prototype.readSurface_), + 'MultiSurface': ol.xml.makeReplacer( + ol.format.GML3.prototype.readMultiSurface_), + 'Curve': ol.xml.makeReplacer(ol.format.GML3.prototype.readCurve_), + 'MultiCurve': ol.xml.makeReplacer( + ol.format.GML3.prototype.readMultiCurve_), + 'Envelope': ol.xml.makeReplacer(ol.format.GML3.prototype.readEnvelope_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.MULTICURVE_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'curveMember': ol.xml.makeArrayPusher( + ol.format.GML3.prototype.curveMemberParser_), + 'curveMembers': ol.xml.makeArrayPusher( + ol.format.GML3.prototype.curveMemberParser_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.MULTISURFACE_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'surfaceMember': ol.xml.makeArrayPusher( + ol.format.GML3.prototype.surfaceMemberParser_), + 'surfaceMembers': ol.xml.makeArrayPusher( + ol.format.GML3.prototype.surfaceMemberParser_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.CURVEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'LineString': ol.xml.makeArrayPusher( + ol.format.GMLBase.prototype.readLineString), + 'Curve': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readCurve_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.SURFACEMEMBER_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'Polygon': ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readPolygon), + 'Surface': ol.xml.makeArrayPusher(ol.format.GML3.prototype.readSurface_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.SURFACE_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'patches': ol.xml.makeReplacer(ol.format.GML3.prototype.readPatch_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.CURVE_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'segments': ol.xml.makeReplacer(ol.format.GML3.prototype.readSegment_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.ENVELOPE_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'lowerCorner': ol.xml.makeArrayPusher( + ol.format.GML3.prototype.readFlatPosList_), + 'upperCorner': ol.xml.makeArrayPusher( + ol.format.GML3.prototype.readFlatPosList_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.PATCHES_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'PolygonPatch': ol.xml.makeReplacer( + ol.format.GML3.prototype.readPolygonPatch_) + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GML3.prototype.SEGMENTS_PARSERS_ = { + 'http://www.opengis.net/gml' : { + 'LineStringSegment': ol.xml.makeReplacer( + ol.format.GML3.prototype.readLineStringSegment_) + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Point} value Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writePos_ = function(node, value, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + var axisOrientation = 'enu'; + if (srsName) { + axisOrientation = ol.proj.get(srsName).getAxisOrientation(); + } + var point = value.getCoordinates(); + var coords; + // only 2d for simple features profile + if (axisOrientation.substr(0, 2) === 'en') { + coords = (point[0] + ' ' + point[1]); + } else { + coords = (point[1] + ' ' + point[0]); + } + ol.format.XSD.writeStringTextNode(node, coords); +}; + + +/** + * @param {Array.<number>} point Point geometry. + * @param {string=} opt_srsName Optional srsName + * @return {string} The coords string. + * @private + */ +ol.format.GML3.prototype.getCoords_ = function(point, opt_srsName) { + var axisOrientation = 'enu'; + if (opt_srsName) { + axisOrientation = ol.proj.get(opt_srsName).getAxisOrientation(); + } + return ((axisOrientation.substr(0, 2) === 'en') ? + point[0] + ' ' + point[1] : + point[1] + ' ' + point[0]); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LineString|ol.geom.LinearRing} value Geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writePosList_ = function(node, value, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + // only 2d for simple features profile + var points = value.getCoordinates(); + var len = points.length; + var parts = new Array(len); + var point; + for (var i = 0; i < len; ++i) { + point = points[i]; + parts[i] = this.getCoords_(point, srsName); + } + ol.format.XSD.writeStringTextNode(node, parts.join(' ')); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Point} geometry Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writePoint_ = function(node, geometry, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + var pos = ol.xml.createElementNS(node.namespaceURI, 'pos'); + node.appendChild(pos); + this.writePos_(pos, geometry, objectStack); +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GML3.ENVELOPE_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lowerCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'upperCorner': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.Extent} extent Extent. + * @param {Array.<*>} objectStack Node stack. + */ +ol.format.GML3.prototype.writeEnvelope = function(node, extent, objectStack) { + goog.asserts.assert(extent.length == 4, 'extent should have 4 items'); + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + var keys = ['lowerCorner', 'upperCorner']; + var values = [extent[0] + ' ' + extent[1], extent[2] + ' ' + extent[3]]; + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ + ({node: node}), ol.format.GML3.ENVELOPE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, + values, + objectStack, keys, this); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LinearRing} geometry LinearRing geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeLinearRing_ = function(node, geometry, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + var posList = ol.xml.createElementNS(node.namespaceURI, 'posList'); + node.appendChild(posList); + this.writePosList_(posList, geometry, objectStack); +}; + + +/** + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node} Node. + * @private + */ +ol.format.GML3.prototype.RING_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { + var context = objectStack[objectStack.length - 1]; + var parentNode = context.node; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var exteriorWritten = context['exteriorWritten']; + if (exteriorWritten === undefined) { + context['exteriorWritten'] = true; + } + return ol.xml.createElementNS(parentNode.namespaceURI, + exteriorWritten !== undefined ? 'interior' : 'exterior'); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Polygon} geometry Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeSurfaceOrPolygon_ = function(node, geometry, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + if (node.nodeName !== 'PolygonPatch' && srsName) { + node.setAttribute('srsName', srsName); + } + if (node.nodeName === 'Polygon' || node.nodeName === 'PolygonPatch') { + var rings = geometry.getLinearRings(); + ol.xml.pushSerializeAndPop( + {node: node, srsName: srsName}, + ol.format.GML3.RING_SERIALIZERS_, + this.RING_NODE_FACTORY_, + rings, objectStack, undefined, this); + } else if (node.nodeName === 'Surface') { + var patches = ol.xml.createElementNS(node.namespaceURI, 'patches'); + node.appendChild(patches); + this.writeSurfacePatches_( + patches, geometry, objectStack); + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LineString} geometry LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeCurveOrLineString_ = function(node, geometry, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + if (node.nodeName !== 'LineStringSegment' && srsName) { + node.setAttribute('srsName', srsName); + } + if (node.nodeName === 'LineString' || + node.nodeName === 'LineStringSegment') { + var posList = ol.xml.createElementNS(node.namespaceURI, 'posList'); + node.appendChild(posList); + this.writePosList_(posList, geometry, objectStack); + } else if (node.nodeName === 'Curve') { + var segments = ol.xml.createElementNS(node.namespaceURI, 'segments'); + node.appendChild(segments); + this.writeCurveSegments_(segments, + geometry, objectStack); + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.MultiPolygon} geometry MultiPolygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_ = function(node, geometry, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + var surface = context['surface']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + var polygons = geometry.getPolygons(); + ol.xml.pushSerializeAndPop({node: node, srsName: srsName, surface: surface}, + ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_, + this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, polygons, + objectStack, undefined, this); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.MultiPoint} geometry MultiPoint geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeMultiPoint_ = function(node, geometry, + objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + var points = geometry.getPoints(); + ol.xml.pushSerializeAndPop({node: node, srsName: srsName}, + ol.format.GML3.POINTMEMBER_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('pointMember'), points, + objectStack, undefined, this); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.MultiLineString} geometry MultiLineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeMultiCurveOrLineString_ = function(node, geometry, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var srsName = context['srsName']; + var curve = context['curve']; + if (srsName) { + node.setAttribute('srsName', srsName); + } + var lines = geometry.getLineStrings(); + ol.xml.pushSerializeAndPop({node: node, srsName: srsName, curve: curve}, + ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_, + this.MULTIGEOMETRY_MEMBER_NODE_FACTORY_, lines, + objectStack, undefined, this); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LinearRing} ring LinearRing geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeRing_ = function(node, ring, objectStack) { + var linearRing = ol.xml.createElementNS(node.namespaceURI, 'LinearRing'); + node.appendChild(linearRing); + this.writeLinearRing_(linearRing, ring, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Polygon} polygon Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeSurfaceOrPolygonMember_ = function(node, polygon, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var child = this.GEOMETRY_NODE_FACTORY_( + polygon, objectStack); + if (child) { + node.appendChild(child); + this.writeSurfaceOrPolygon_(child, polygon, objectStack); + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Point} point Point geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writePointMember_ = function(node, point, objectStack) { + var child = ol.xml.createElementNS(node.namespaceURI, 'Point'); + node.appendChild(child); + this.writePoint_(child, point, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LineString} line LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeLineStringOrCurveMember_ = function(node, line, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var child = this.GEOMETRY_NODE_FACTORY_(line, objectStack); + if (child) { + node.appendChild(child); + this.writeCurveOrLineString_(child, line, objectStack); + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Polygon} polygon Polygon geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeSurfacePatches_ = function(node, polygon, objectStack) { + var child = ol.xml.createElementNS(node.namespaceURI, 'PolygonPatch'); + node.appendChild(child); + this.writeSurfaceOrPolygon_(child, polygon, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LineString} line LineString geometry. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeCurveSegments_ = function(node, line, objectStack) { + var child = ol.xml.createElementNS(node.namespaceURI, + 'LineStringSegment'); + node.appendChild(child); + this.writeCurveOrLineString_(child, line, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Geometry|ol.Extent} geometry Geometry. + * @param {Array.<*>} objectStack Node stack. + */ +ol.format.GML3.prototype.writeGeometryElement = function(node, geometry, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var item = ol.object.assign({}, context); + item.node = node; + var value; + if (Array.isArray(geometry)) { + if (context.dataProjection) { + value = ol.proj.transformExtent( + geometry, context.featureProjection, context.dataProjection); + } else { + value = geometry; + } + } else { + goog.asserts.assertInstanceof(geometry, ol.geom.Geometry, + 'geometry should be an ol.geom.Geometry'); + value = + ol.format.Feature.transformWithOptions(geometry, true, context); + } + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ + (item), ol.format.GML3.GEOMETRY_SERIALIZERS_, + this.GEOMETRY_NODE_FACTORY_, [value], + objectStack, undefined, this); +}; + + +/** + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Node stack. + */ +ol.format.GML3.prototype.writeFeatureElement = function(node, feature, objectStack) { + var fid = feature.getId(); + if (fid) { + node.setAttribute('fid', fid); + } + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var featureNS = context['featureNS']; + var geometryName = feature.getGeometryName(); + if (!context.serializers) { + context.serializers = {}; + context.serializers[featureNS] = {}; + } + var properties = feature.getProperties(); + var keys = [], values = []; + for (var key in properties) { + var value = properties[key]; + if (value !== null) { + keys.push(key); + values.push(value); + if (key == geometryName || value instanceof ol.geom.Geometry) { + if (!(key in context.serializers[featureNS])) { + context.serializers[featureNS][key] = ol.xml.makeChildAppender( + this.writeGeometryElement, this); + } + } else { + if (!(key in context.serializers[featureNS])) { + context.serializers[featureNS][key] = ol.xml.makeChildAppender( + ol.format.XSD.writeStringTextNode); + } + } + } + } + var item = ol.object.assign({}, context); + item.node = node; + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ + (item), context.serializers, + ol.xml.makeSimpleNodeFactory(undefined, featureNS), + values, + objectStack, keys); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<ol.Feature>} features Features. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GML3.prototype.writeFeatureMembers_ = function(node, features, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var featureType = context['featureType']; + var featureNS = context['featureNS']; + var serializers = {}; + serializers[featureNS] = {}; + serializers[featureNS][featureType] = ol.xml.makeChildAppender( + this.writeFeatureElement, this); + var item = ol.object.assign({}, context); + item.node = node; + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ + (item), + serializers, + ol.xml.makeSimpleNodeFactory(featureType, featureNS), features, + objectStack); +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GML3.SURFACEORPOLYGONMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'surfaceMember': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeSurfaceOrPolygonMember_), + 'polygonMember': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeSurfaceOrPolygonMember_) + } +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GML3.POINTMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'pointMember': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writePointMember_) + } +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GML3.LINESTRINGORCURVEMEMBER_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'lineStringMember': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeLineStringOrCurveMember_), + 'curveMember': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeLineStringOrCurveMember_) + } +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GML3.RING_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'exterior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_), + 'interior': ol.xml.makeChildAppender(ol.format.GML3.prototype.writeRing_) + } +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GML3.GEOMETRY_SERIALIZERS_ = { + 'http://www.opengis.net/gml': { + 'Curve': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeCurveOrLineString_), + 'MultiCurve': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeMultiCurveOrLineString_), + 'Point': ol.xml.makeChildAppender(ol.format.GML3.prototype.writePoint_), + 'MultiPoint': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeMultiPoint_), + 'LineString': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeCurveOrLineString_), + 'MultiLineString': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeMultiCurveOrLineString_), + 'LinearRing': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeLinearRing_), + 'Polygon': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeSurfaceOrPolygon_), + 'MultiPolygon': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_), + 'Surface': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeSurfaceOrPolygon_), + 'MultiSurface': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeMultiSurfaceOrPolygon_), + 'Envelope': ol.xml.makeChildAppender( + ol.format.GML3.prototype.writeEnvelope) + } +}; + + +/** + * @const + * @type {Object.<string, string>} + * @private + */ +ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_ = { + 'MultiLineString': 'lineStringMember', + 'MultiCurve': 'curveMember', + 'MultiPolygon': 'polygonMember', + 'MultiSurface': 'surfaceMember' +}; + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.GML3.prototype.MULTIGEOMETRY_MEMBER_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { + var parentNode = objectStack[objectStack.length - 1].node; + goog.asserts.assert(ol.xml.isNode(parentNode), + 'parentNode should be a node'); + return ol.xml.createElementNS('http://www.opengis.net/gml', + ol.format.GML3.MULTIGEOMETRY_TO_MEMBER_NODENAME_[parentNode.nodeName]); +}; + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.GML3.prototype.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var multiSurface = context['multiSurface']; + var surface = context['surface']; + var curve = context['curve']; + var multiCurve = context['multiCurve']; + var parentNode = objectStack[objectStack.length - 1].node; + goog.asserts.assert(ol.xml.isNode(parentNode), + 'parentNode should be a node'); + var nodeName; + if (!Array.isArray(value)) { + goog.asserts.assertInstanceof(value, ol.geom.Geometry, + 'value should be an ol.geom.Geometry'); + nodeName = value.getType(); + if (nodeName === 'MultiPolygon' && multiSurface === true) { + nodeName = 'MultiSurface'; + } else if (nodeName === 'Polygon' && surface === true) { + nodeName = 'Surface'; + } else if (nodeName === 'LineString' && curve === true) { + nodeName = 'Curve'; + } else if (nodeName === 'MultiLineString' && multiCurve === true) { + nodeName = 'MultiCurve'; + } + } else { + nodeName = 'Envelope'; + } + return ol.xml.createElementNS('http://www.opengis.net/gml', + nodeName); +}; + + +/** + * Encode a geometry in GML 3.1.1 Simple Features. + * + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {Node} Node. + * @api + */ +ol.format.GML3.prototype.writeGeometryNode = function(geometry, opt_options) { + opt_options = this.adaptOptions(opt_options); + var geom = ol.xml.createElementNS('http://www.opengis.net/gml', 'geom'); + var context = {node: geom, srsName: this.srsName, + curve: this.curve_, surface: this.surface_, + multiSurface: this.multiSurface_, multiCurve: this.multiCurve_}; + if (opt_options) { + ol.object.assign(context, opt_options); + } + this.writeGeometryElement(geom, geometry, [context]); + return geom; +}; + + +/** + * Encode an array of features in GML 3.1.1 Simple Features. + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {string} Result. + * @api stable + */ +ol.format.GML3.prototype.writeFeatures; + + +/** + * Encode an array of features in the GML 3.1.1 format as an XML node. + * + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {Node} Node. + * @api + */ +ol.format.GML3.prototype.writeFeaturesNode = function(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + var node = ol.xml.createElementNS('http://www.opengis.net/gml', + 'featureMembers'); + ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation', this.schemaLocation); + var context = { + srsName: this.srsName, + curve: this.curve_, + surface: this.surface_, + multiSurface: this.multiSurface_, + multiCurve: this.multiCurve_, + featureNS: this.featureNS, + featureType: this.featureType + }; + if (opt_options) { + ol.object.assign(context, opt_options); + } + this.writeFeatureMembers_(node, features, [context]); + return node; +}; + + +/** + * @classdesc + * Feature format for reading and writing data in the GML format + * version 3.1.1. + * Currently only supports GML 3.1.1 Simple Features profile. + * + * @constructor + * @param {olx.format.GMLOptions=} opt_options + * Optional configuration object. + * @extends {ol.format.GMLBase} + * @api stable + */ +ol.format.GML = ol.format.GML3; + + +/** + * Encode an array of features in GML 3.1.1 Simple Features. + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {string} Result. + * @api stable + */ +ol.format.GML.prototype.writeFeatures; + + +/** + * Encode an array of features in the GML 3.1.1 format as an XML node. + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {Node} Node. + * @api + */ +ol.format.GML.prototype.writeFeaturesNode; + +goog.provide('ol.format.GPX'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.array'); +goog.require('ol.format.Feature'); +goog.require('ol.format.XMLFeature'); +goog.require('ol.format.XSD'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.Point'); +goog.require('ol.proj'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Feature format for reading and writing data in the GPX format. + * + * @constructor + * @extends {ol.format.XMLFeature} + * @param {olx.format.GPXOptions=} opt_options Options. + * @api stable + */ +ol.format.GPX = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.XMLFeature.call(this); + + /** + * @inheritDoc + */ + this.defaultDataProjection = ol.proj.get('EPSG:4326'); + + /** + * @type {function(ol.Feature, Node)|undefined} + * @private + */ + this.readExtensions_ = options.readExtensions; +}; +ol.inherits(ol.format.GPX, ol.format.XMLFeature); + + +/** + * @const + * @private + * @type {Array.<string>} + */ +ol.format.GPX.NAMESPACE_URIS_ = [ + null, + 'http://www.topografix.com/GPX/1/0', + 'http://www.topografix.com/GPX/1/1' +]; + + +/** + * @const + * @type {string} + * @private + */ +ol.format.GPX.SCHEMA_LOCATION_ = 'http://www.topografix.com/GPX/1/1 ' + + 'http://www.topografix.com/GPX/1/1/gpx.xsd'; + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {Node} node Node. + * @param {Object} values Values. + * @private + * @return {Array.<number>} Flat coordinates. + */ +ol.format.GPX.appendCoordinate_ = function(flatCoordinates, node, values) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + flatCoordinates.push( + parseFloat(node.getAttribute('lon')), + parseFloat(node.getAttribute('lat'))); + if ('ele' in values) { + flatCoordinates.push(/** @type {number} */ (values['ele'])); + delete values['ele']; + } else { + flatCoordinates.push(0); + } + if ('time' in values) { + flatCoordinates.push(/** @type {number} */ (values['time'])); + delete values['time']; + } else { + flatCoordinates.push(0); + } + return flatCoordinates; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.parseLink_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'link', 'localName should be link'); + var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); + var href = node.getAttribute('href'); + if (href !== null) { + values['link'] = href; + } + ol.xml.parseNode(ol.format.GPX.LINK_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.parseExtensions_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'extensions', + 'localName should be extensions'); + var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); + values['extensionsNode_'] = node; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.parseRtePt_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'rtept', 'localName should be rtept'); + var values = ol.xml.pushParseAndPop( + {}, ol.format.GPX.RTEPT_PARSERS_, node, objectStack); + if (values) { + var rteValues = /** @type {Object} */ (objectStack[objectStack.length - 1]); + var flatCoordinates = /** @type {Array.<number>} */ + (rteValues['flatCoordinates']); + ol.format.GPX.appendCoordinate_(flatCoordinates, node, values); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.parseTrkPt_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'trkpt', 'localName should be trkpt'); + var values = ol.xml.pushParseAndPop( + {}, ol.format.GPX.TRKPT_PARSERS_, node, objectStack); + if (values) { + var trkValues = /** @type {Object} */ (objectStack[objectStack.length - 1]); + var flatCoordinates = /** @type {Array.<number>} */ + (trkValues['flatCoordinates']); + ol.format.GPX.appendCoordinate_(flatCoordinates, node, values); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.parseTrkSeg_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'trkseg', + 'localName should be trkseg'); + var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); + ol.xml.parseNode(ol.format.GPX.TRKSEG_PARSERS_, node, objectStack); + var flatCoordinates = /** @type {Array.<number>} */ + (values['flatCoordinates']); + var ends = /** @type {Array.<number>} */ (values['ends']); + ends.push(flatCoordinates.length); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.Feature|undefined} Track. + */ +ol.format.GPX.readRte_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'rte', 'localName should be rte'); + var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); + var values = ol.xml.pushParseAndPop({ + 'flatCoordinates': [] + }, ol.format.GPX.RTE_PARSERS_, node, objectStack); + if (!values) { + return undefined; + } + var flatCoordinates = /** @type {Array.<number>} */ + (values['flatCoordinates']); + delete values['flatCoordinates']; + var geometry = new ol.geom.LineString(null); + geometry.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates); + ol.format.Feature.transformWithOptions(geometry, false, options); + var feature = new ol.Feature(geometry); + feature.setProperties(values); + return feature; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.Feature|undefined} Track. + */ +ol.format.GPX.readTrk_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'trk', 'localName should be trk'); + var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); + var values = ol.xml.pushParseAndPop({ + 'flatCoordinates': [], + 'ends': [] + }, ol.format.GPX.TRK_PARSERS_, node, objectStack); + if (!values) { + return undefined; + } + var flatCoordinates = /** @type {Array.<number>} */ + (values['flatCoordinates']); + delete values['flatCoordinates']; + var ends = /** @type {Array.<number>} */ (values['ends']); + delete values['ends']; + var geometry = new ol.geom.MultiLineString(null); + geometry.setFlatCoordinates( + ol.geom.GeometryLayout.XYZM, flatCoordinates, ends); + ol.format.Feature.transformWithOptions(geometry, false, options); + var feature = new ol.Feature(geometry); + feature.setProperties(values); + return feature; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.Feature|undefined} Waypoint. + */ +ol.format.GPX.readWpt_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'wpt', 'localName should be wpt'); + var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); + var values = ol.xml.pushParseAndPop( + {}, ol.format.GPX.WPT_PARSERS_, node, objectStack); + if (!values) { + return undefined; + } + var coordinates = ol.format.GPX.appendCoordinate_([], node, values); + var geometry = new ol.geom.Point( + coordinates, ol.geom.GeometryLayout.XYZM); + ol.format.Feature.transformWithOptions(geometry, false, options); + var feature = new ol.Feature(geometry); + feature.setProperties(values); + return feature; +}; + + +/** + * @const + * @type {Object.<string, function(Node, Array.<*>): (ol.Feature|undefined)>} + * @private + */ +ol.format.GPX.FEATURE_READER_ = { + 'rte': ol.format.GPX.readRte_, + 'trk': ol.format.GPX.readTrk_, + 'wpt': ol.format.GPX.readWpt_ +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.GPX_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'rte': ol.xml.makeArrayPusher(ol.format.GPX.readRte_), + 'trk': ol.xml.makeArrayPusher(ol.format.GPX.readTrk_), + 'wpt': ol.xml.makeArrayPusher(ol.format.GPX.readWpt_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.LINK_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'text': + ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkText'), + 'type': + ol.xml.makeObjectPropertySetter(ol.format.XSD.readString, 'linkType') + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.RTE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'link': ol.format.GPX.parseLink_, + 'number': + ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger), + 'extensions': ol.format.GPX.parseExtensions_, + 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'rtept': ol.format.GPX.parseRtePt_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.RTEPT_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.TRK_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'link': ol.format.GPX.parseLink_, + 'number': + ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger), + 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'extensions': ol.format.GPX.parseExtensions_, + 'trkseg': ol.format.GPX.parseTrkSeg_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.TRKSEG_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'trkpt': ol.format.GPX.parseTrkPt_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.TRKPT_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.GPX.WPT_PARSERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'ele': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'time': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDateTime), + 'magvar': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'geoidheight': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'cmt': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'desc': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'src': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'link': ol.format.GPX.parseLink_, + 'sym': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'type': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'fix': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'sat': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'hdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'vdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'pdop': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'ageofdgpsdata': + ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'dgpsid': + ol.xml.makeObjectPropertySetter(ol.format.XSD.readNonNegativeInteger), + 'extensions': ol.format.GPX.parseExtensions_ + }); + + +/** + * @param {Array.<ol.Feature>} features List of features. + * @private + */ +ol.format.GPX.prototype.handleReadExtensions_ = function(features) { + if (!features) { + features = []; + } + for (var i = 0, ii = features.length; i < ii; ++i) { + var feature = features[i]; + if (this.readExtensions_) { + var extensionsNode = feature.get('extensionsNode_') || null; + this.readExtensions_(feature, extensionsNode); + } + feature.set('extensionsNode_', undefined); + } +}; + + +/** + * Read the first feature from a GPX source. + * Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`) + * into MultiLineString. Any properties on route and track waypoints are ignored. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @api stable + */ +ol.format.GPX.prototype.readFeature; + + +/** + * @inheritDoc + */ +ol.format.GPX.prototype.readFeatureFromNode = function(node, opt_options) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + if (!ol.array.includes(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) { + return null; + } + var featureReader = ol.format.GPX.FEATURE_READER_[node.localName]; + if (!featureReader) { + return null; + } + var feature = featureReader(node, [this.getReadOptions(node, opt_options)]); + if (!feature) { + return null; + } + this.handleReadExtensions_([feature]); + return feature; +}; + + +/** + * Read all features from a GPX source. + * Routes (`<rte>`) are converted into LineString geometries, and tracks (`<trk>`) + * into MultiLineString. Any properties on route and track waypoints are ignored. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.GPX.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.GPX.prototype.readFeaturesFromNode = function(node, opt_options) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + if (!ol.array.includes(ol.format.GPX.NAMESPACE_URIS_, node.namespaceURI)) { + return []; + } + if (node.localName == 'gpx') { + /** @type {Array.<ol.Feature>} */ + var features = ol.xml.pushParseAndPop([], ol.format.GPX.GPX_PARSERS_, + node, [this.getReadOptions(node, opt_options)]); + if (features) { + this.handleReadExtensions_(features); + return features; + } else { + return []; + } + } + return []; +}; + + +/** + * Read the projection from a GPX source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + * @api stable + */ +ol.format.GPX.prototype.readProjection; + + +/** + * @param {Node} node Node. + * @param {string} value Value for the link's `href` attribute. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.GPX.writeLink_ = function(node, value, objectStack) { + node.setAttribute('href', value); + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var properties = context['properties']; + var link = [ + properties['linkText'], + properties['linkType'] + ]; + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ ({node: node}), + ol.format.GPX.LINK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, + link, objectStack, ol.format.GPX.LINK_SEQUENCE_); +}; + + +/** + * @param {Node} node Node. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.writeWptType_ = function(node, coordinate, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var parentNode = context.node; + goog.asserts.assert(ol.xml.isNode(parentNode), + 'parentNode should be an XML node'); + var namespaceURI = parentNode.namespaceURI; + var properties = context['properties']; + //FIXME Projection handling + ol.xml.setAttributeNS(node, null, 'lat', coordinate[1]); + ol.xml.setAttributeNS(node, null, 'lon', coordinate[0]); + var geometryLayout = context['geometryLayout']; + switch (geometryLayout) { + case ol.geom.GeometryLayout.XYZM: + if (coordinate[3] !== 0) { + properties['time'] = coordinate[3]; + } + // fall through + case ol.geom.GeometryLayout.XYZ: + if (coordinate[2] !== 0) { + properties['ele'] = coordinate[2]; + } + break; + case ol.geom.GeometryLayout.XYM: + if (coordinate[2] !== 0) { + properties['time'] = coordinate[2]; + } + break; + default: + // pass + } + var orderedKeys = (node.nodeName == 'rtept') ? + ol.format.GPX.RTEPT_TYPE_SEQUENCE_[namespaceURI] : + ol.format.GPX.WPT_TYPE_SEQUENCE_[namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ + ({node: node, 'properties': properties}), + ol.format.GPX.WPT_TYPE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, + values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.writeRte_ = function(node, feature, objectStack) { + var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); + var properties = feature.getProperties(); + var context = {node: node, 'properties': properties}; + var geometry = feature.getGeometry(); + if (geometry) { + goog.asserts.assertInstanceof(geometry, ol.geom.LineString, + 'geometry should be an ol.geom.LineString'); + geometry = /** @type {ol.geom.LineString} */ + (ol.format.Feature.transformWithOptions(geometry, true, options)); + context['geometryLayout'] = geometry.getLayout(); + properties['rtept'] = geometry.getCoordinates(); + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.GPX.RTE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, + ol.format.GPX.RTE_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, + values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.writeTrk_ = function(node, feature, objectStack) { + var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); + var properties = feature.getProperties(); + /** @type {ol.XmlNodeStackItem} */ + var context = {node: node, 'properties': properties}; + var geometry = feature.getGeometry(); + if (geometry) { + goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString, + 'geometry should be an ol.geom.MultiLineString'); + geometry = /** @type {ol.geom.MultiLineString} */ + (ol.format.Feature.transformWithOptions(geometry, true, options)); + properties['trkseg'] = geometry.getLineStrings(); + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.GPX.TRK_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, + ol.format.GPX.TRK_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, + values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LineString} lineString LineString. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.writeTrkSeg_ = function(node, lineString, objectStack) { + /** @type {ol.XmlNodeStackItem} */ + var context = {node: node, 'geometryLayout': lineString.getLayout(), + 'properties': {}}; + ol.xml.pushSerializeAndPop(context, + ol.format.GPX.TRKSEG_SERIALIZERS_, ol.format.GPX.TRKSEG_NODE_FACTORY_, + lineString.getCoordinates(), objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.GPX.writeWpt_ = function(node, feature, objectStack) { + var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + context['properties'] = feature.getProperties(); + var geometry = feature.getGeometry(); + if (geometry) { + goog.asserts.assertInstanceof(geometry, ol.geom.Point, + 'geometry should be an ol.geom.Point'); + geometry = /** @type {ol.geom.Point} */ + (ol.format.Feature.transformWithOptions(geometry, true, options)); + context['geometryLayout'] = geometry.getLayout(); + ol.format.GPX.writeWptType_(node, geometry.getCoordinates(), objectStack); + } +}; + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.GPX.LINK_SEQUENCE_ = ['text', 'type']; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GPX.LINK_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'text': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) + }); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.GPX.RTE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, [ + 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'rtept' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GPX.RTE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_), + 'number': ol.xml.makeChildAppender( + ol.format.XSD.writeNonNegativeIntegerTextNode), + 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'rtept': ol.xml.makeArraySerializer(ol.xml.makeChildAppender( + ol.format.GPX.writeWptType_)) + }); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.GPX.RTEPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, [ + 'ele', 'time' + ]); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.GPX.TRK_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, [ + 'name', 'cmt', 'desc', 'src', 'link', 'number', 'type', 'trkseg' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GPX.TRK_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_), + 'number': ol.xml.makeChildAppender( + ol.format.XSD.writeNonNegativeIntegerTextNode), + 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'trkseg': ol.xml.makeArraySerializer(ol.xml.makeChildAppender( + ol.format.GPX.writeTrkSeg_)) + }); + + +/** + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.GPX.TRKSEG_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('trkpt'); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GPX.TRKSEG_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'trkpt': ol.xml.makeChildAppender(ol.format.GPX.writeWptType_) + }); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.GPX.WPT_TYPE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, [ + 'ele', 'time', 'magvar', 'geoidheight', 'name', 'cmt', 'desc', 'src', + 'link', 'sym', 'type', 'fix', 'sat', 'hdop', 'vdop', 'pdop', + 'ageofdgpsdata', 'dgpsid' + ]); + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GPX.WPT_TYPE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'ele': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'time': ol.xml.makeChildAppender(ol.format.XSD.writeDateTimeTextNode), + 'magvar': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'geoidheight': ol.xml.makeChildAppender( + ol.format.XSD.writeDecimalTextNode), + 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'cmt': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'desc': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'src': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'link': ol.xml.makeChildAppender(ol.format.GPX.writeLink_), + 'sym': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'type': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'fix': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'sat': ol.xml.makeChildAppender( + ol.format.XSD.writeNonNegativeIntegerTextNode), + 'hdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'vdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'pdop': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'ageofdgpsdata': ol.xml.makeChildAppender( + ol.format.XSD.writeDecimalTextNode), + 'dgpsid': ol.xml.makeChildAppender( + ol.format.XSD.writeNonNegativeIntegerTextNode) + }); + + +/** + * @const + * @type {Object.<string, string>} + * @private + */ +ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_ = { + 'Point': 'wpt', + 'LineString': 'rte', + 'MultiLineString': 'trk' +}; + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.GPX.GPX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { + goog.asserts.assertInstanceof(value, ol.Feature, + 'value should be an ol.Feature'); + var geometry = value.getGeometry(); + if (geometry) { + var nodeName = ol.format.GPX.GEOMETRY_TYPE_TO_NODENAME_[geometry.getType()]; + if (nodeName) { + var parentNode = objectStack[objectStack.length - 1].node; + goog.asserts.assert(ol.xml.isNode(parentNode), + 'parentNode should be an XML node'); + return ol.xml.createElementNS(parentNode.namespaceURI, nodeName); + } + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.GPX.GPX_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.GPX.NAMESPACE_URIS_, { + 'rte': ol.xml.makeChildAppender(ol.format.GPX.writeRte_), + 'trk': ol.xml.makeChildAppender(ol.format.GPX.writeTrk_), + 'wpt': ol.xml.makeChildAppender(ol.format.GPX.writeWpt_) + }); + + +/** + * Encode an array of features in the GPX format. + * LineString geometries are output as routes (`<rte>`), and MultiLineString + * as tracks (`<trk>`). + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} Result. + * @api stable + */ +ol.format.GPX.prototype.writeFeatures; + + +/** + * Encode an array of features in the GPX format as an XML node. + * LineString geometries are output as routes (`<rte>`), and MultiLineString + * as tracks (`<trk>`). + * + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {Node} Node. + * @api + */ +ol.format.GPX.prototype.writeFeaturesNode = function(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + //FIXME Serialize metadata + var gpx = ol.xml.createElementNS('http://www.topografix.com/GPX/1/1', 'gpx'); + var xmlnsUri = 'http://www.w3.org/2000/xmlns/'; + var xmlSchemaInstanceUri = 'http://www.w3.org/2001/XMLSchema-instance'; + ol.xml.setAttributeNS(gpx, xmlnsUri, 'xmlns:xsi', xmlSchemaInstanceUri); + ol.xml.setAttributeNS(gpx, xmlSchemaInstanceUri, 'xsi:schemaLocation', + ol.format.GPX.SCHEMA_LOCATION_); + gpx.setAttribute('version', '1.1'); + gpx.setAttribute('creator', 'OpenLayers 3'); + + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ + ({node: gpx}), ol.format.GPX.GPX_SERIALIZERS_, + ol.format.GPX.GPX_NODE_FACTORY_, features, [opt_options]); + return gpx; +}; + +goog.provide('ol.format.TextFeature'); + +goog.require('goog.asserts'); +goog.require('ol.format.Feature'); +goog.require('ol.format.FormatType'); + + +/** + * @classdesc + * Abstract base class; normally only used for creating subclasses and not + * instantiated in apps. + * Base class for text feature formats. + * + * @constructor + * @extends {ol.format.Feature} + */ +ol.format.TextFeature = function() { + ol.format.Feature.call(this); +}; +ol.inherits(ol.format.TextFeature, ol.format.Feature); + + +/** + * @param {Document|Node|Object|string} source Source. + * @private + * @return {string} Text. + */ +ol.format.TextFeature.prototype.getText_ = function(source) { + if (typeof source === 'string') { + return source; + } else { + goog.asserts.fail(); + return ''; + } +}; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.getType = function() { + return ol.format.FormatType.TEXT; +}; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.readFeature = function(source, opt_options) { + return this.readFeatureFromText( + this.getText_(source), this.adaptOptions(opt_options)); +}; + + +/** + * @param {string} text Text. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @protected + * @return {ol.Feature} Feature. + */ +ol.format.TextFeature.prototype.readFeatureFromText = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.readFeatures = function(source, opt_options) { + return this.readFeaturesFromText( + this.getText_(source), this.adaptOptions(opt_options)); +}; + + +/** + * @param {string} text Text. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @protected + * @return {Array.<ol.Feature>} Features. + */ +ol.format.TextFeature.prototype.readFeaturesFromText = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.readGeometry = function(source, opt_options) { + return this.readGeometryFromText( + this.getText_(source), this.adaptOptions(opt_options)); +}; + + +/** + * @param {string} text Text. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @protected + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.TextFeature.prototype.readGeometryFromText = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.readProjection = function(source) { + return this.readProjectionFromText(this.getText_(source)); +}; + + +/** + * @param {string} text Text. + * @protected + * @return {ol.proj.Projection} Projection. + */ +ol.format.TextFeature.prototype.readProjectionFromText = function(text) { + return this.defaultDataProjection; +}; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.writeFeature = function(feature, opt_options) { + return this.writeFeatureText(feature, this.adaptOptions(opt_options)); +}; + + +/** + * @param {ol.Feature} feature Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ +ol.format.TextFeature.prototype.writeFeatureText = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.writeFeatures = function( + features, opt_options) { + return this.writeFeaturesText(features, this.adaptOptions(opt_options)); +}; + + +/** + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ +ol.format.TextFeature.prototype.writeFeaturesText = goog.abstractMethod; + + +/** + * @inheritDoc + */ +ol.format.TextFeature.prototype.writeGeometry = function( + geometry, opt_options) { + return this.writeGeometryText(geometry, this.adaptOptions(opt_options)); +}; + + +/** + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @protected + * @return {string} Text. + */ +ol.format.TextFeature.prototype.writeGeometryText = goog.abstractMethod; + +goog.provide('ol.format.IGC'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.format.Feature'); +goog.require('ol.format.TextFeature'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.LineString'); +goog.require('ol.proj'); + + +/** + * IGC altitude/z. One of 'barometric', 'gps', 'none'. + * @enum {string} + */ +ol.format.IGCZ = { + BAROMETRIC: 'barometric', + GPS: 'gps', + NONE: 'none' +}; + + +/** + * @classdesc + * Feature format for `*.igc` flight recording files. + * + * @constructor + * @extends {ol.format.TextFeature} + * @param {olx.format.IGCOptions=} opt_options Options. + * @api + */ +ol.format.IGC = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.TextFeature.call(this); + + /** + * @inheritDoc + */ + this.defaultDataProjection = ol.proj.get('EPSG:4326'); + + /** + * @private + * @type {ol.format.IGCZ} + */ + this.altitudeMode_ = options.altitudeMode ? + options.altitudeMode : ol.format.IGCZ.NONE; + +}; +ol.inherits(ol.format.IGC, ol.format.TextFeature); + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.IGC.EXTENSIONS_ = ['.igc']; + + +/** + * @const + * @type {RegExp} + * @private + */ +ol.format.IGC.B_RECORD_RE_ = + /^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/; + + +/** + * @const + * @type {RegExp} + * @private + */ +ol.format.IGC.H_RECORD_RE_ = /^H.([A-Z]{3}).*?:(.*)/; + + +/** + * @const + * @type {RegExp} + * @private + */ +ol.format.IGC.HFDTE_RECORD_RE_ = /^HFDTE(\d{2})(\d{2})(\d{2})/; + + +/** + * A regular expression matching the newline characters `\r\n`, `\r` and `\n`. + * + * @const + * @type {RegExp} + * @private + */ +ol.format.IGC.NEWLINE_RE_ = /\r\n|\r|\n/; + + +/** + * @inheritDoc + */ +ol.format.IGC.prototype.getExtensions = function() { + return ol.format.IGC.EXTENSIONS_; +}; + + +/** + * Read the feature from the IGC source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @api + */ +ol.format.IGC.prototype.readFeature; + + +/** + * @inheritDoc + */ +ol.format.IGC.prototype.readFeatureFromText = function(text, opt_options) { + var altitudeMode = this.altitudeMode_; + var lines = text.split(ol.format.IGC.NEWLINE_RE_); + /** @type {Object.<string, string>} */ + var properties = {}; + var flatCoordinates = []; + var year = 2000; + var month = 0; + var day = 1; + var lastDateTime = -1; + var i, ii; + for (i = 0, ii = lines.length; i < ii; ++i) { + var line = lines[i]; + var m; + if (line.charAt(0) == 'B') { + m = ol.format.IGC.B_RECORD_RE_.exec(line); + if (m) { + var hour = parseInt(m[1], 10); + var minute = parseInt(m[2], 10); + var second = parseInt(m[3], 10); + var y = parseInt(m[4], 10) + parseInt(m[5], 10) / 60000; + if (m[6] == 'S') { + y = -y; + } + var x = parseInt(m[7], 10) + parseInt(m[8], 10) / 60000; + if (m[9] == 'W') { + x = -x; + } + flatCoordinates.push(x, y); + if (altitudeMode != ol.format.IGCZ.NONE) { + var z; + if (altitudeMode == ol.format.IGCZ.GPS) { + z = parseInt(m[11], 10); + } else if (altitudeMode == ol.format.IGCZ.BAROMETRIC) { + z = parseInt(m[12], 10); + } else { + goog.asserts.fail(); + z = 0; + } + flatCoordinates.push(z); + } + var dateTime = Date.UTC(year, month, day, hour, minute, second); + // Detect UTC midnight wrap around. + if (dateTime < lastDateTime) { + dateTime = Date.UTC(year, month, day + 1, hour, minute, second); + } + flatCoordinates.push(dateTime / 1000); + lastDateTime = dateTime; + } + } else if (line.charAt(0) == 'H') { + m = ol.format.IGC.HFDTE_RECORD_RE_.exec(line); + if (m) { + day = parseInt(m[1], 10); + month = parseInt(m[2], 10) - 1; + year = 2000 + parseInt(m[3], 10); + } else { + m = ol.format.IGC.H_RECORD_RE_.exec(line); + if (m) { + properties[m[1]] = m[2].trim(); + } + } + } + } + if (flatCoordinates.length === 0) { + return null; + } + var lineString = new ol.geom.LineString(null); + var layout = altitudeMode == ol.format.IGCZ.NONE ? + ol.geom.GeometryLayout.XYM : ol.geom.GeometryLayout.XYZM; + lineString.setFlatCoordinates(layout, flatCoordinates); + var feature = new ol.Feature(ol.format.Feature.transformWithOptions( + lineString, false, opt_options)); + feature.setProperties(properties); + return feature; +}; + + +/** + * Read the feature from the source. As IGC sources contain a single + * feature, this will return the feature in an array. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api + */ +ol.format.IGC.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.IGC.prototype.readFeaturesFromText = function(text, opt_options) { + var feature = this.readFeatureFromText(text, opt_options); + if (feature) { + return [feature]; + } else { + return []; + } +}; + + +/** + * Read the projection from the IGC source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + * @api + */ +ol.format.IGC.prototype.readProjection; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Generics method for collection-like classes and objects. + * + * @author arv@google.com (Erik Arvidsson) + * + * This file contains functions to work with collections. It supports using + * Map, Set, Array and Object and other classes that implement collection-like + * methods. + */ + + +goog.provide('goog.structs'); + +goog.require('goog.array'); +goog.require('goog.object'); + + +// We treat an object as a dictionary if it has getKeys or it is an object that +// isn't arrayLike. + + +/** + * Returns the number of values in the collection-like object. + * @param {Object} col The collection-like object. + * @return {number} The number of values in the collection-like object. + */ +goog.structs.getCount = function(col) { + if (col.getCount && typeof col.getCount == 'function') { + return col.getCount(); + } + if (goog.isArrayLike(col) || goog.isString(col)) { + return col.length; + } + return goog.object.getCount(col); +}; + + +/** + * Returns the values of the collection-like object. + * @param {Object} col The collection-like object. + * @return {!Array<?>} The values in the collection-like object. + */ +goog.structs.getValues = function(col) { + if (col.getValues && typeof col.getValues == 'function') { + return col.getValues(); + } + if (goog.isString(col)) { + return col.split(''); + } + if (goog.isArrayLike(col)) { + var rv = []; + var l = col.length; + for (var i = 0; i < l; i++) { + rv.push(col[i]); + } + return rv; + } + return goog.object.getValues(col); +}; + + +/** + * Returns the keys of the collection. Some collections have no notion of + * keys/indexes and this function will return undefined in those cases. + * @param {Object} col The collection-like object. + * @return {!Array|undefined} The keys in the collection. + */ +goog.structs.getKeys = function(col) { + if (col.getKeys && typeof col.getKeys == 'function') { + return col.getKeys(); + } + // if we have getValues but no getKeys we know this is a key-less collection + if (col.getValues && typeof col.getValues == 'function') { + return undefined; + } + if (goog.isArrayLike(col) || goog.isString(col)) { + var rv = []; + var l = col.length; + for (var i = 0; i < l; i++) { + rv.push(i); + } + return rv; + } + + return goog.object.getKeys(col); +}; + + +/** + * Whether the collection contains the given value. This is O(n) and uses + * equals (==) to test the existence. + * @param {Object} col The collection-like object. + * @param {*} val The value to check for. + * @return {boolean} True if the map contains the value. + */ +goog.structs.contains = function(col, val) { + if (col.contains && typeof col.contains == 'function') { + return col.contains(val); + } + if (col.containsValue && typeof col.containsValue == 'function') { + return col.containsValue(val); + } + if (goog.isArrayLike(col) || goog.isString(col)) { + return goog.array.contains(/** @type {!Array<?>} */ (col), val); + } + return goog.object.containsValue(col, val); +}; + + +/** + * Whether the collection is empty. + * @param {Object} col The collection-like object. + * @return {boolean} True if empty. + */ +goog.structs.isEmpty = function(col) { + if (col.isEmpty && typeof col.isEmpty == 'function') { + return col.isEmpty(); + } + + // We do not use goog.string.isEmptyOrWhitespace because here we treat the + // string as + // collection and as such even whitespace matters + + if (goog.isArrayLike(col) || goog.isString(col)) { + return goog.array.isEmpty(/** @type {!Array<?>} */ (col)); + } + return goog.object.isEmpty(col); +}; + + +/** + * Removes all the elements from the collection. + * @param {Object} col The collection-like object. + */ +goog.structs.clear = function(col) { + // NOTE(arv): This should not contain strings because strings are immutable + if (col.clear && typeof col.clear == 'function') { + col.clear(); + } else if (goog.isArrayLike(col)) { + goog.array.clear(/** @type {IArrayLike<?>} */ (col)); + } else { + goog.object.clear(col); + } +}; + + +/** + * Calls a function for each value in a collection. The function takes + * three arguments; the value, the key and the collection. + * + * NOTE: This will be deprecated soon! Please use a more specific method if + * possible, e.g. goog.array.forEach, goog.object.forEach, etc. + * + * @param {S} col The collection-like object. + * @param {function(this:T,?,?,S):?} f The function to call for every value. + * This function takes + * 3 arguments (the value, the key or undefined if the collection has no + * notion of keys, and the collection) and the return value is irrelevant. + * @param {T=} opt_obj The object to be used as the value of 'this' + * within {@code f}. + * @template T,S + */ +goog.structs.forEach = function(col, f, opt_obj) { + if (col.forEach && typeof col.forEach == 'function') { + col.forEach(f, opt_obj); + } else if (goog.isArrayLike(col) || goog.isString(col)) { + goog.array.forEach(/** @type {!Array<?>} */ (col), f, opt_obj); + } else { + var keys = goog.structs.getKeys(col); + var values = goog.structs.getValues(col); + var l = values.length; + for (var i = 0; i < l; i++) { + f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col); + } + } +}; + + +/** + * Calls a function for every value in the collection. When a call returns true, + * adds the value to a new collection (Array is returned by default). + * + * @param {S} col The collection-like object. + * @param {function(this:T,?,?,S):boolean} f The function to call for every + * value. This function takes + * 3 arguments (the value, the key or undefined if the collection has no + * notion of keys, and the collection) and should return a Boolean. If the + * return value is true the value is added to the result collection. If it + * is false the value is not included. + * @param {T=} opt_obj The object to be used as the value of 'this' + * within {@code f}. + * @return {!Object|!Array<?>} A new collection where the passed values are + * present. If col is a key-less collection an array is returned. If col + * has keys and values a plain old JS object is returned. + * @template T,S + */ +goog.structs.filter = function(col, f, opt_obj) { + if (typeof col.filter == 'function') { + return col.filter(f, opt_obj); + } + if (goog.isArrayLike(col) || goog.isString(col)) { + return goog.array.filter(/** @type {!Array<?>} */ (col), f, opt_obj); + } + + var rv; + var keys = goog.structs.getKeys(col); + var values = goog.structs.getValues(col); + var l = values.length; + if (keys) { + rv = {}; + for (var i = 0; i < l; i++) { + if (f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col)) { + rv[keys[i]] = values[i]; + } + } + } else { + // We should not use goog.array.filter here since we want to make sure that + // the index is undefined as well as make sure that col is passed to the + // function. + rv = []; + for (var i = 0; i < l; i++) { + if (f.call(opt_obj, values[i], undefined, col)) { + rv.push(values[i]); + } + } + } + return rv; +}; + + +/** + * Calls a function for every value in the collection and adds the result into a + * new collection (defaults to creating a new Array). + * + * @param {S} col The collection-like object. + * @param {function(this:T,?,?,S):V} f The function to call for every value. + * This function takes 3 arguments (the value, the key or undefined if the + * collection has no notion of keys, and the collection) and should return + * something. The result will be used as the value in the new collection. + * @param {T=} opt_obj The object to be used as the value of 'this' + * within {@code f}. + * @return {!Object<V>|!Array<V>} A new collection with the new values. If + * col is a key-less collection an array is returned. If col has keys and + * values a plain old JS object is returned. + * @template T,S,V + */ +goog.structs.map = function(col, f, opt_obj) { + if (typeof col.map == 'function') { + return col.map(f, opt_obj); + } + if (goog.isArrayLike(col) || goog.isString(col)) { + return goog.array.map(/** @type {!Array<?>} */ (col), f, opt_obj); + } + + var rv; + var keys = goog.structs.getKeys(col); + var values = goog.structs.getValues(col); + var l = values.length; + if (keys) { + rv = {}; + for (var i = 0; i < l; i++) { + rv[keys[i]] = f.call(/** @type {?} */ (opt_obj), values[i], keys[i], col); + } + } else { + // We should not use goog.array.map here since we want to make sure that + // the index is undefined as well as make sure that col is passed to the + // function. + rv = []; + for (var i = 0; i < l; i++) { + rv[i] = f.call(/** @type {?} */ (opt_obj), values[i], undefined, col); + } + } + return rv; +}; + + +/** + * Calls f for each value in a collection. If any call returns true this returns + * true (without checking the rest). If all returns false this returns false. + * + * @param {S} col The collection-like object. + * @param {function(this:T,?,?,S):boolean} f The function to call for every + * value. This function takes 3 arguments (the value, the key or undefined + * if the collection has no notion of keys, and the collection) and should + * return a boolean. + * @param {T=} opt_obj The object to be used as the value of 'this' + * within {@code f}. + * @return {boolean} True if any value passes the test. + * @template T,S + */ +goog.structs.some = function(col, f, opt_obj) { + if (typeof col.some == 'function') { + return col.some(f, opt_obj); + } + if (goog.isArrayLike(col) || goog.isString(col)) { + return goog.array.some(/** @type {!Array<?>} */ (col), f, opt_obj); + } + var keys = goog.structs.getKeys(col); + var values = goog.structs.getValues(col); + var l = values.length; + for (var i = 0; i < l; i++) { + if (f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) { + return true; + } + } + return false; +}; + + +/** + * Calls f for each value in a collection. If all calls return true this return + * true this returns true. If any returns false this returns false at this point + * and does not continue to check the remaining values. + * + * @param {S} col The collection-like object. + * @param {function(this:T,?,?,S):boolean} f The function to call for every + * value. This function takes 3 arguments (the value, the key or + * undefined if the collection has no notion of keys, and the collection) + * and should return a boolean. + * @param {T=} opt_obj The object to be used as the value of 'this' + * within {@code f}. + * @return {boolean} True if all key-value pairs pass the test. + * @template T,S + */ +goog.structs.every = function(col, f, opt_obj) { + if (typeof col.every == 'function') { + return col.every(f, opt_obj); + } + if (goog.isArrayLike(col) || goog.isString(col)) { + return goog.array.every(/** @type {!Array<?>} */ (col), f, opt_obj); + } + var keys = goog.structs.getKeys(col); + var values = goog.structs.getValues(col); + var l = values.length; + for (var i = 0; i < l; i++) { + if (!f.call(/** @type {?} */ (opt_obj), values[i], keys && keys[i], col)) { + return false; + } + } + return true; +}; + +// Copyright 2007 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Python style iteration utilities. + * @author arv@google.com (Erik Arvidsson) + */ + + +goog.provide('goog.iter'); +goog.provide('goog.iter.Iterable'); +goog.provide('goog.iter.Iterator'); +goog.provide('goog.iter.StopIteration'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.functions'); +goog.require('goog.math'); + + +/** + * @typedef {goog.iter.Iterator|{length:number}|{__iterator__}} + */ +goog.iter.Iterable; + + +/** + * Singleton Error object that is used to terminate iterations. + * @const {!Error} + */ +goog.iter.StopIteration = ('StopIteration' in goog.global) ? + // For script engines that support legacy iterators. + goog.global['StopIteration'] : + {message: 'StopIteration', stack: ''}; + + + +/** + * Class/interface for iterators. An iterator needs to implement a {@code next} + * method and it needs to throw a {@code goog.iter.StopIteration} when the + * iteration passes beyond the end. Iterators have no {@code hasNext} method. + * It is recommended to always use the helper functions to iterate over the + * iterator or in case you are only targeting JavaScript 1.7 for in loops. + * @constructor + * @template VALUE + */ +goog.iter.Iterator = function() {}; + + +/** + * Returns the next value of the iteration. This will throw the object + * {@see goog.iter#StopIteration} when the iteration passes the end. + * @return {VALUE} Any object or value. + */ +goog.iter.Iterator.prototype.next = function() { + throw goog.iter.StopIteration; +}; + + +/** + * Returns the {@code Iterator} object itself. This is used to implement + * the iterator protocol in JavaScript 1.7 + * @param {boolean=} opt_keys Whether to return the keys or values. Default is + * to only return the values. This is being used by the for-in loop (true) + * and the for-each-in loop (false). Even though the param gives a hint + * about what the iterator will return there is no guarantee that it will + * return the keys when true is passed. + * @return {!goog.iter.Iterator<VALUE>} The object itself. + */ +goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) { + return this; +}; + + +/** + * Returns an iterator that knows how to iterate over the values in the object. + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable If the + * object is an iterator it will be returned as is. If the object has an + * {@code __iterator__} method that will be called to get the value + * iterator. If the object is an array-like object we create an iterator + * for that. + * @return {!goog.iter.Iterator<VALUE>} An iterator that knows how to iterate + * over the values in {@code iterable}. + * @template VALUE + */ +goog.iter.toIterator = function(iterable) { + if (iterable instanceof goog.iter.Iterator) { + return iterable; + } + if (typeof iterable.__iterator__ == 'function') { + return iterable.__iterator__(false); + } + if (goog.isArrayLike(iterable)) { + var i = 0; + var newIter = new goog.iter.Iterator; + newIter.next = function() { + while (true) { + if (i >= iterable.length) { + throw goog.iter.StopIteration; + } + // Don't include deleted elements. + if (!(i in iterable)) { + i++; + continue; + } + return iterable[i++]; + } + }; + return newIter; + } + + + // TODO(arv): Should we fall back on goog.structs.getValues()? + throw Error('Not implemented'); +}; + + +/** + * Calls a function for each element in the iterator with the element of the + * iterator passed as argument. + * + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * to iterate over. If the iterable is an object {@code toIterator} will be + * called on it. + * @param {function(this:THIS,VALUE,?,!goog.iter.Iterator<VALUE>)} f + * The function to call for every element. This function takes 3 arguments + * (the element, undefined, and the iterator) and the return value is + * irrelevant. The reason for passing undefined as the second argument is + * so that the same function can be used in {@see goog.array#forEach} as + * well as others. The third parameter is of type "number" for + * arraylike objects, undefined, otherwise. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @template THIS, VALUE + */ +goog.iter.forEach = function(iterable, f, opt_obj) { + if (goog.isArrayLike(iterable)) { + /** @preserveTry */ + try { + // NOTES: this passes the index number to the second parameter + // of the callback contrary to the documentation above. + goog.array.forEach( + /** @type {IArrayLike<?>} */ (iterable), f, opt_obj); + } catch (ex) { + if (ex !== goog.iter.StopIteration) { + throw ex; + } + } + } else { + iterable = goog.iter.toIterator(iterable); + /** @preserveTry */ + try { + while (true) { + f.call(opt_obj, iterable.next(), undefined, iterable); + } + } catch (ex) { + if (ex !== goog.iter.StopIteration) { + throw ex; + } + } + } +}; + + +/** + * Calls a function for every element in the iterator, and if the function + * returns true adds the element to a new iterator. + * + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * to iterate over. + * @param { + * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f + * The function to call for every element. This function takes 3 arguments + * (the element, undefined, and the iterator) and should return a boolean. + * If the return value is true the element will be included in the returned + * iterator. If it is false the element is not included. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements + * that passed the test are present. + * @template THIS, VALUE + */ +goog.iter.filter = function(iterable, f, opt_obj) { + var iterator = goog.iter.toIterator(iterable); + var newIter = new goog.iter.Iterator; + newIter.next = function() { + while (true) { + var val = iterator.next(); + if (f.call(opt_obj, val, undefined, iterator)) { + return val; + } + } + }; + return newIter; +}; + + +/** + * Calls a function for every element in the iterator, and if the function + * returns false adds the element to a new iterator. + * + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * to iterate over. + * @param { + * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f + * The function to call for every element. This function takes 3 arguments + * (the element, undefined, and the iterator) and should return a boolean. + * If the return value is false the element will be included in the returned + * iterator. If it is true the element is not included. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @return {!goog.iter.Iterator<VALUE>} A new iterator in which only elements + * that did not pass the test are present. + * @template THIS, VALUE + */ +goog.iter.filterFalse = function(iterable, f, opt_obj) { + return goog.iter.filter(iterable, goog.functions.not(f), opt_obj); +}; + + +/** + * Creates a new iterator that returns the values in a range. This function + * can take 1, 2 or 3 arguments: + * <pre> + * range(5) same as range(0, 5, 1) + * range(2, 5) same as range(2, 5, 1) + * </pre> + * + * @param {number} startOrStop The stop value if only one argument is provided. + * The start value if 2 or more arguments are provided. If only one + * argument is used the start value is 0. + * @param {number=} opt_stop The stop value. If left out then the first + * argument is used as the stop value. + * @param {number=} opt_step The number to increment with between each call to + * next. This can be negative. + * @return {!goog.iter.Iterator<number>} A new iterator that returns the values + * in the range. + */ +goog.iter.range = function(startOrStop, opt_stop, opt_step) { + var start = 0; + var stop = startOrStop; + var step = opt_step || 1; + if (arguments.length > 1) { + start = startOrStop; + stop = opt_stop; + } + if (step == 0) { + throw Error('Range step argument must not be zero'); + } + + var newIter = new goog.iter.Iterator; + newIter.next = function() { + if (step > 0 && start >= stop || step < 0 && start <= stop) { + throw goog.iter.StopIteration; + } + var rv = start; + start += step; + return rv; + }; + return newIter; +}; + + +/** + * Joins the values in a iterator with a delimiter. + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * to get the values from. + * @param {string} deliminator The text to put between the values. + * @return {string} The joined value string. + * @template VALUE + */ +goog.iter.join = function(iterable, deliminator) { + return goog.iter.toArray(iterable).join(deliminator); +}; + + +/** + * For every element in the iterator call a function and return a new iterator + * with that value. + * + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterator to iterate over. + * @param { + * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):RESULT} f + * The function to call for every element. This function takes 3 arguments + * (the element, undefined, and the iterator) and should return a new value. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the + * results of applying the function to each element in the original + * iterator. + * @template THIS, VALUE, RESULT + */ +goog.iter.map = function(iterable, f, opt_obj) { + var iterator = goog.iter.toIterator(iterable); + var newIter = new goog.iter.Iterator; + newIter.next = function() { + var val = iterator.next(); + return f.call(opt_obj, val, undefined, iterator); + }; + return newIter; +}; + + +/** + * Passes every element of an iterator into a function and accumulates the + * result. + * + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * to iterate over. + * @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for + * every element. This function takes 2 arguments (the function's previous + * result or the initial value, and the value of the current element). + * function(previousValue, currentElement) : newValue. + * @param {VALUE} val The initial value to pass into the function on the first + * call. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * f. + * @return {VALUE} Result of evaluating f repeatedly across the values of + * the iterator. + * @template THIS, VALUE + */ +goog.iter.reduce = function(iterable, f, val, opt_obj) { + var rval = val; + goog.iter.forEach( + iterable, function(val) { rval = f.call(opt_obj, rval, val); }); + return rval; +}; + + +/** + * Goes through the values in the iterator. Calls f for each of these, and if + * any of them returns true, this returns true (without checking the rest). If + * all return false this will return false. + * + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * object. + * @param { + * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f + * The function to call for every value. This function takes 3 arguments + * (the value, undefined, and the iterator) and should return a boolean. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @return {boolean} true if any value passes the test. + * @template THIS, VALUE + */ +goog.iter.some = function(iterable, f, opt_obj) { + iterable = goog.iter.toIterator(iterable); + /** @preserveTry */ + try { + while (true) { + if (f.call(opt_obj, iterable.next(), undefined, iterable)) { + return true; + } + } + } catch (ex) { + if (ex !== goog.iter.StopIteration) { + throw ex; + } + } + return false; +}; + + +/** + * Goes through the values in the iterator. Calls f for each of these and if any + * of them returns false this returns false (without checking the rest). If all + * return true this will return true. + * + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * object. + * @param { + * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f + * The function to call for every value. This function takes 3 arguments + * (the value, undefined, and the iterator) and should return a boolean. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @return {boolean} true if every value passes the test. + * @template THIS, VALUE + */ +goog.iter.every = function(iterable, f, opt_obj) { + iterable = goog.iter.toIterator(iterable); + /** @preserveTry */ + try { + while (true) { + if (!f.call(opt_obj, iterable.next(), undefined, iterable)) { + return false; + } + } + } catch (ex) { + if (ex !== goog.iter.StopIteration) { + throw ex; + } + } + return true; +}; + + +/** + * Takes zero or more iterables and returns one iterator that will iterate over + * them in the order chained. + * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any + * number of iterable objects. + * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will + * iterate over all the given iterables' contents. + * @template VALUE + */ +goog.iter.chain = function(var_args) { + return goog.iter.chainFromIterable(arguments); +}; + + +/** + * Takes a single iterable containing zero or more iterables and returns one + * iterator that will iterate over each one in the order given. + * @see https://goo.gl/5NRp5d + * @param {goog.iter.Iterable} iterable The iterable of iterables to chain. + * @return {!goog.iter.Iterator<VALUE>} Returns a new iterator that will + * iterate over all the contents of the iterables contained within + * {@code iterable}. + * @template VALUE + */ +goog.iter.chainFromIterable = function(iterable) { + var iterator = goog.iter.toIterator(iterable); + var iter = new goog.iter.Iterator(); + var current = null; + + iter.next = function() { + while (true) { + if (current == null) { + var it = iterator.next(); + current = goog.iter.toIterator(it); + } + try { + return current.next(); + } catch (ex) { + if (ex !== goog.iter.StopIteration) { + throw ex; + } + current = null; + } + } + }; + + return iter; +}; + + +/** + * Builds a new iterator that iterates over the original, but skips elements as + * long as a supplied function returns true. + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * object. + * @param { + * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f + * The function to call for every value. This function takes 3 arguments + * (the value, undefined, and the iterator) and should return a boolean. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @return {!goog.iter.Iterator<VALUE>} A new iterator that drops elements from + * the original iterator as long as {@code f} is true. + * @template THIS, VALUE + */ +goog.iter.dropWhile = function(iterable, f, opt_obj) { + var iterator = goog.iter.toIterator(iterable); + var newIter = new goog.iter.Iterator; + var dropping = true; + newIter.next = function() { + while (true) { + var val = iterator.next(); + if (dropping && f.call(opt_obj, val, undefined, iterator)) { + continue; + } else { + dropping = false; + } + return val; + } + }; + return newIter; +}; + + +/** + * Builds a new iterator that iterates over the original, but only as long as a + * supplied function returns true. + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * object. + * @param { + * function(this:THIS,VALUE,undefined,!goog.iter.Iterator<VALUE>):boolean} f + * The function to call for every value. This function takes 3 arguments + * (the value, undefined, and the iterator) and should return a boolean. + * @param {THIS=} opt_obj This is used as the 'this' object in f when called. + * @return {!goog.iter.Iterator<VALUE>} A new iterator that keeps elements in + * the original iterator as long as the function is true. + * @template THIS, VALUE + */ +goog.iter.takeWhile = function(iterable, f, opt_obj) { + var iterator = goog.iter.toIterator(iterable); + var iter = new goog.iter.Iterator(); + iter.next = function() { + var val = iterator.next(); + if (f.call(opt_obj, val, undefined, iterator)) { + return val; + } + throw goog.iter.StopIteration; + }; + return iter; +}; + + +/** + * Converts the iterator to an array + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterator + * to convert to an array. + * @return {!Array<VALUE>} An array of the elements the iterator iterates over. + * @template VALUE + */ +goog.iter.toArray = function(iterable) { + // Fast path for array-like. + if (goog.isArrayLike(iterable)) { + return goog.array.toArray(/** @type {!IArrayLike<?>} */ (iterable)); + } + iterable = goog.iter.toIterator(iterable); + var array = []; + goog.iter.forEach(iterable, function(val) { array.push(val); }); + return array; +}; + + +/** + * Iterates over two iterables and returns true if they contain the same + * sequence of elements and have the same length. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable1 The first + * iterable object. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable2 The second + * iterable object. + * @param {function(VALUE,VALUE):boolean=} opt_equalsFn Optional comparison + * function. + * Should take two arguments to compare, and return true if the arguments + * are equal. Defaults to {@link goog.array.defaultCompareEquality} which + * compares the elements using the built-in '===' operator. + * @return {boolean} true if the iterables contain the same sequence of elements + * and have the same length. + * @template VALUE + */ +goog.iter.equals = function(iterable1, iterable2, opt_equalsFn) { + var fillValue = {}; + var pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2); + var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality; + return goog.iter.every( + pairs, function(pair) { return equalsFn(pair[0], pair[1]); }); +}; + + +/** + * Advances the iterator to the next position, returning the given default value + * instead of throwing an exception if the iterator has no more entries. + * @param {goog.iter.Iterator<VALUE>|goog.iter.Iterable} iterable The iterable + * object. + * @param {VALUE} defaultValue The value to return if the iterator is empty. + * @return {VALUE} The next item in the iteration, or defaultValue if the + * iterator was empty. + * @template VALUE + */ +goog.iter.nextOrValue = function(iterable, defaultValue) { + try { + return goog.iter.toIterator(iterable).next(); + } catch (e) { + if (e != goog.iter.StopIteration) { + throw e; + } + return defaultValue; + } +}; + + +/** + * Cartesian product of zero or more sets. Gives an iterator that gives every + * combination of one element chosen from each set. For example, + * ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]). + * @see http://docs.python.org/library/itertools.html#itertools.product + * @param {...!IArrayLike<VALUE>} var_args Zero or more sets, as + * arrays. + * @return {!goog.iter.Iterator<!Array<VALUE>>} An iterator that gives each + * n-tuple (as an array). + * @template VALUE + */ +goog.iter.product = function(var_args) { + var someArrayEmpty = + goog.array.some(arguments, function(arr) { return !arr.length; }); + + // An empty set in a cartesian product gives an empty set. + if (someArrayEmpty || !arguments.length) { + return new goog.iter.Iterator(); + } + + var iter = new goog.iter.Iterator(); + var arrays = arguments; + + // The first indices are [0, 0, ...] + var indicies = goog.array.repeat(0, arrays.length); + + iter.next = function() { + + if (indicies) { + var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) { + return arrays[arrayIndex][valueIndex]; + }); + + // Generate the next-largest indices for the next call. + // Increase the rightmost index. If it goes over, increase the next + // rightmost (like carry-over addition). + for (var i = indicies.length - 1; i >= 0; i--) { + // Assertion prevents compiler warning below. + goog.asserts.assert(indicies); + if (indicies[i] < arrays[i].length - 1) { + indicies[i]++; + break; + } + + // We're at the last indices (the last element of every array), so + // the iteration is over on the next call. + if (i == 0) { + indicies = null; + break; + } + // Reset the index in this column and loop back to increment the + // next one. + indicies[i] = 0; + } + return retVal; + } + + throw goog.iter.StopIteration; + }; + + return iter; +}; + + +/** + * Create an iterator to cycle over the iterable's elements indefinitely. + * For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ... + * @see: http://docs.python.org/library/itertools.html#itertools.cycle. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable object. + * @return {!goog.iter.Iterator<VALUE>} An iterator that iterates indefinitely + * over the values in {@code iterable}. + * @template VALUE + */ +goog.iter.cycle = function(iterable) { + var baseIterator = goog.iter.toIterator(iterable); + + // We maintain a cache to store the iterable elements as we iterate + // over them. The cache is used to return elements once we have + // iterated over the iterable once. + var cache = []; + var cacheIndex = 0; + + var iter = new goog.iter.Iterator(); + + // This flag is set after the iterable is iterated over once + var useCache = false; + + iter.next = function() { + var returnElement = null; + + // Pull elements off the original iterator if not using cache + if (!useCache) { + try { + // Return the element from the iterable + returnElement = baseIterator.next(); + cache.push(returnElement); + return returnElement; + } catch (e) { + // If an exception other than StopIteration is thrown + // or if there are no elements to iterate over (the iterable was empty) + // throw an exception + if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) { + throw e; + } + // set useCache to true after we know that a 'StopIteration' exception + // was thrown and the cache is not empty (to handle the 'empty iterable' + // use case) + useCache = true; + } + } + + returnElement = cache[cacheIndex]; + cacheIndex = (cacheIndex + 1) % cache.length; + + return returnElement; + }; + + return iter; +}; + + +/** + * Creates an iterator that counts indefinitely from a starting value. + * @see http://docs.python.org/2/library/itertools.html#itertools.count + * @param {number=} opt_start The starting value. Default is 0. + * @param {number=} opt_step The number to increment with between each call to + * next. Negative and floating point numbers are allowed. Default is 1. + * @return {!goog.iter.Iterator<number>} A new iterator that returns the values + * in the series. + */ +goog.iter.count = function(opt_start, opt_step) { + var counter = opt_start || 0; + var step = goog.isDef(opt_step) ? opt_step : 1; + var iter = new goog.iter.Iterator(); + + iter.next = function() { + var returnValue = counter; + counter += step; + return returnValue; + }; + + return iter; +}; + + +/** + * Creates an iterator that returns the same object or value repeatedly. + * @param {VALUE} value Any object or value to repeat. + * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the + * repeated value. + * @template VALUE + */ +goog.iter.repeat = function(value) { + var iter = new goog.iter.Iterator(); + + iter.next = goog.functions.constant(value); + + return iter; +}; + + +/** + * Creates an iterator that returns running totals from the numbers in + * {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields + * {@code 1 -> 3 -> 6 -> 10 -> 15}. + * @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate + * @param {!goog.iter.Iterable<number>} iterable The iterable of numbers to + * accumulate. + * @return {!goog.iter.Iterator<number>} A new iterator that returns the + * numbers in the series. + */ +goog.iter.accumulate = function(iterable) { + var iterator = goog.iter.toIterator(iterable); + var total = 0; + var iter = new goog.iter.Iterator(); + + iter.next = function() { + total += iterator.next(); + return total; + }; + + return iter; +}; + + +/** + * Creates an iterator that returns arrays containing the ith elements from the + * provided iterables. The returned arrays will be the same size as the number + * of iterables given in {@code var_args}. Once the shortest iterable is + * exhausted, subsequent calls to {@code next()} will throw + * {@code goog.iter.StopIteration}. + * @see http://docs.python.org/2/library/itertools.html#itertools.izip + * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any + * number of iterable objects. + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns + * arrays of elements from the provided iterables. + * @template VALUE + */ +goog.iter.zip = function(var_args) { + var args = arguments; + var iter = new goog.iter.Iterator(); + + if (args.length > 0) { + var iterators = goog.array.map(args, goog.iter.toIterator); + iter.next = function() { + var arr = goog.array.map(iterators, function(it) { return it.next(); }); + return arr; + }; + } + + return iter; +}; + + +/** + * Creates an iterator that returns arrays containing the ith elements from the + * provided iterables. The returned arrays will be the same size as the number + * of iterables given in {@code var_args}. Shorter iterables will be extended + * with {@code fillValue}. Once the longest iterable is exhausted, subsequent + * calls to {@code next()} will throw {@code goog.iter.StopIteration}. + * @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest + * @param {VALUE} fillValue The object or value used to fill shorter iterables. + * @param {...!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} var_args Any + * number of iterable objects. + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator that returns + * arrays of elements from the provided iterables. + * @template VALUE + */ +goog.iter.zipLongest = function(fillValue, var_args) { + var args = goog.array.slice(arguments, 1); + var iter = new goog.iter.Iterator(); + + if (args.length > 0) { + var iterators = goog.array.map(args, goog.iter.toIterator); + + iter.next = function() { + var iteratorsHaveValues = false; // false when all iterators are empty. + var arr = goog.array.map(iterators, function(it) { + var returnValue; + try { + returnValue = it.next(); + // Iterator had a value, so we've not exhausted the iterators. + // Set flag accordingly. + iteratorsHaveValues = true; + } catch (ex) { + if (ex !== goog.iter.StopIteration) { + throw ex; + } + returnValue = fillValue; + } + return returnValue; + }); + + if (!iteratorsHaveValues) { + throw goog.iter.StopIteration; + } + return arr; + }; + } + + return iter; +}; + + +/** + * Creates an iterator that filters {@code iterable} based on a series of + * {@code selectors}. On each call to {@code next()}, one item is taken from + * both the {@code iterable} and {@code selectors} iterators. If the item from + * {@code selectors} evaluates to true, the item from {@code iterable} is given. + * Otherwise, it is skipped. Once either {@code iterable} or {@code selectors} + * is exhausted, subsequent calls to {@code next()} will throw + * {@code goog.iter.StopIteration}. + * @see http://docs.python.org/2/library/itertools.html#itertools.compress + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to filter. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} selectors An + * iterable of items to be evaluated in a boolean context to determine if + * the corresponding element in {@code iterable} should be included in the + * result. + * @return {!goog.iter.Iterator<VALUE>} A new iterator that returns the + * filtered values. + * @template VALUE + */ +goog.iter.compress = function(iterable, selectors) { + var selectorIterator = goog.iter.toIterator(selectors); + + return goog.iter.filter( + iterable, function() { return !!selectorIterator.next(); }); +}; + + + +/** + * Implements the {@code goog.iter.groupBy} iterator. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to group. + * @param {function(VALUE): KEY=} opt_keyFunc Optional function for + * determining the key value for each group in the {@code iterable}. Default + * is the identity function. + * @constructor + * @extends {goog.iter.Iterator<!Array<?>>} + * @template KEY, VALUE + * @private + */ +goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) { + + /** + * The iterable to group, coerced to an iterator. + * @type {!goog.iter.Iterator} + */ + this.iterator = goog.iter.toIterator(iterable); + + /** + * A function for determining the key value for each element in the iterable. + * If no function is provided, the identity function is used and returns the + * element unchanged. + * @type {function(VALUE): KEY} + */ + this.keyFunc = opt_keyFunc || goog.functions.identity; + + /** + * The target key for determining the start of a group. + * @type {KEY} + */ + this.targetKey; + + /** + * The current key visited during iteration. + * @type {KEY} + */ + this.currentKey; + + /** + * The current value being added to the group. + * @type {VALUE} + */ + this.currentValue; +}; +goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator); + + +/** @override */ +goog.iter.GroupByIterator_.prototype.next = function() { + while (this.currentKey == this.targetKey) { + this.currentValue = this.iterator.next(); // Exits on StopIteration + this.currentKey = this.keyFunc(this.currentValue); + } + this.targetKey = this.currentKey; + return [this.currentKey, this.groupItems_(this.targetKey)]; +}; + + +/** + * Performs the grouping of objects using the given key. + * @param {KEY} targetKey The target key object for the group. + * @return {!Array<VALUE>} An array of grouped objects. + * @private + */ +goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) { + var arr = []; + while (this.currentKey == targetKey) { + arr.push(this.currentValue); + try { + this.currentValue = this.iterator.next(); + } catch (ex) { + if (ex !== goog.iter.StopIteration) { + throw ex; + } + break; + } + this.currentKey = this.keyFunc(this.currentValue); + } + return arr; +}; + + +/** + * Creates an iterator that returns arrays containing elements from the + * {@code iterable} grouped by a key value. For iterables with repeated + * elements (i.e. sorted according to a particular key function), this function + * has a {@code uniq}-like effect. For example, grouping the array: + * {@code [A, B, B, C, C, A]} produces + * {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}. + * @see http://docs.python.org/2/library/itertools.html#itertools.groupby + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to group. + * @param {function(VALUE): KEY=} opt_keyFunc Optional function for + * determining the key value for each group in the {@code iterable}. Default + * is the identity function. + * @return {!goog.iter.Iterator<!Array<?>>} A new iterator that returns + * arrays of consecutive key and groups. + * @template KEY, VALUE + */ +goog.iter.groupBy = function(iterable, opt_keyFunc) { + return new goog.iter.GroupByIterator_(iterable, opt_keyFunc); +}; + + +/** + * Gives an iterator that gives the result of calling the given function + * <code>f</code> with the arguments taken from the next element from + * <code>iterable</code> (the elements are expected to also be iterables). + * + * Similar to {@see goog.iter#map} but allows the function to accept multiple + * arguments from the iterable. + * + * @param {!goog.iter.Iterable<!goog.iter.Iterable>} iterable The iterable of + * iterables to iterate over. + * @param {function(this:THIS,...*):RESULT} f The function to call for every + * element. This function takes N+2 arguments, where N represents the + * number of items from the next element of the iterable. The two + * additional arguments passed to the function are undefined and the + * iterator itself. The function should return a new value. + * @param {THIS=} opt_obj The object to be used as the value of 'this' within + * {@code f}. + * @return {!goog.iter.Iterator<RESULT>} A new iterator that returns the + * results of applying the function to each element in the original + * iterator. + * @template THIS, RESULT + */ +goog.iter.starMap = function(iterable, f, opt_obj) { + var iterator = goog.iter.toIterator(iterable); + var iter = new goog.iter.Iterator(); + + iter.next = function() { + var args = goog.iter.toArray(iterator.next()); + return f.apply(opt_obj, goog.array.concat(args, undefined, iterator)); + }; + + return iter; +}; + + +/** + * Returns an array of iterators each of which can iterate over the values in + * {@code iterable} without advancing the others. + * @see http://docs.python.org/2/library/itertools.html#itertools.tee + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to tee. + * @param {number=} opt_num The number of iterators to create. Default is 2. + * @return {!Array<goog.iter.Iterator<VALUE>>} An array of iterators. + * @template VALUE + */ +goog.iter.tee = function(iterable, opt_num) { + var iterator = goog.iter.toIterator(iterable); + var num = goog.isNumber(opt_num) ? opt_num : 2; + var buffers = + goog.array.map(goog.array.range(num), function() { return []; }); + + var addNextIteratorValueToBuffers = function() { + var val = iterator.next(); + goog.array.forEach(buffers, function(buffer) { buffer.push(val); }); + }; + + var createIterator = function(buffer) { + // Each tee'd iterator has an associated buffer (initially empty). When a + // tee'd iterator's buffer is empty, it calls + // addNextIteratorValueToBuffers(), adding the next value to all tee'd + // iterators' buffers, and then returns that value. This allows each + // iterator to be advanced independently. + var iter = new goog.iter.Iterator(); + + iter.next = function() { + if (goog.array.isEmpty(buffer)) { + addNextIteratorValueToBuffers(); + } + goog.asserts.assert(!goog.array.isEmpty(buffer)); + return buffer.shift(); + }; + + return iter; + }; + + return goog.array.map(buffers, createIterator); +}; + + +/** + * Creates an iterator that returns arrays containing a count and an element + * obtained from the given {@code iterable}. + * @see http://docs.python.org/2/library/functions.html#enumerate + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to enumerate. + * @param {number=} opt_start Optional starting value. Default is 0. + * @return {!goog.iter.Iterator<!Array<?>>} A new iterator containing + * count/item pairs. + * @template VALUE + */ +goog.iter.enumerate = function(iterable, opt_start) { + return goog.iter.zip(goog.iter.count(opt_start), iterable); +}; + + +/** + * Creates an iterator that returns the first {@code limitSize} elements from an + * iterable. If this number is greater than the number of elements in the + * iterable, all the elements are returned. + * @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to limit. + * @param {number} limitSize The maximum number of elements to return. + * @return {!goog.iter.Iterator<VALUE>} A new iterator containing + * {@code limitSize} elements. + * @template VALUE + */ +goog.iter.limit = function(iterable, limitSize) { + goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0); + + var iterator = goog.iter.toIterator(iterable); + + var iter = new goog.iter.Iterator(); + var remaining = limitSize; + + iter.next = function() { + if (remaining-- > 0) { + return iterator.next(); + } + throw goog.iter.StopIteration; + }; + + return iter; +}; + + +/** + * Creates an iterator that is advanced {@code count} steps ahead. Consumed + * values are silently discarded. If {@code count} is greater than the number + * of elements in {@code iterable}, an empty iterator is returned. Subsequent + * calls to {@code next()} will throw {@code goog.iter.StopIteration}. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to consume. + * @param {number} count The number of elements to consume from the iterator. + * @return {!goog.iter.Iterator<VALUE>} An iterator advanced zero or more steps + * ahead. + * @template VALUE + */ +goog.iter.consume = function(iterable, count) { + goog.asserts.assert(goog.math.isInt(count) && count >= 0); + + var iterator = goog.iter.toIterator(iterable); + + while (count-- > 0) { + goog.iter.nextOrValue(iterator, null); + } + + return iterator; +}; + + +/** + * Creates an iterator that returns a range of elements from an iterable. + * Similar to {@see goog.array#slice} but does not support negative indexes. + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to slice. + * @param {number} start The index of the first element to return. + * @param {number=} opt_end The index after the last element to return. If + * defined, must be greater than or equal to {@code start}. + * @return {!goog.iter.Iterator<VALUE>} A new iterator containing a slice of + * the original. + * @template VALUE + */ +goog.iter.slice = function(iterable, start, opt_end) { + goog.asserts.assert(goog.math.isInt(start) && start >= 0); + + var iterator = goog.iter.consume(iterable, start); + + if (goog.isNumber(opt_end)) { + goog.asserts.assert(goog.math.isInt(opt_end) && opt_end >= start); + iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */); + } + + return iterator; +}; + + +/** + * Checks an array for duplicate elements. + * @param {?IArrayLike<VALUE>} arr The array to check for + * duplicates. + * @return {boolean} True, if the array contains duplicates, false otherwise. + * @private + * @template VALUE + */ +// TODO(user): Consider moving this into goog.array as a public function. +goog.iter.hasDuplicates_ = function(arr) { + var deduped = []; + goog.array.removeDuplicates(arr, deduped); + return arr.length != deduped.length; +}; + + +/** + * Creates an iterator that returns permutations of elements in + * {@code iterable}. + * + * Permutations are obtained by taking the Cartesian product of + * {@code opt_length} iterables and filtering out those with repeated + * elements. For example, the permutations of {@code [1,2,3]} are + * {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}. + * @see http://docs.python.org/2/library/itertools.html#itertools.permutations + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable from which to generate permutations. + * @param {number=} opt_length Length of each permutation. If omitted, defaults + * to the length of {@code iterable}. + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing the + * permutations of {@code iterable}. + * @template VALUE + */ +goog.iter.permutations = function(iterable, opt_length) { + var elements = goog.iter.toArray(iterable); + var length = goog.isNumber(opt_length) ? opt_length : elements.length; + + var sets = goog.array.repeat(elements, length); + var product = goog.iter.product.apply(undefined, sets); + + return goog.iter.filter( + product, function(arr) { return !goog.iter.hasDuplicates_(arr); }); +}; + + +/** + * Creates an iterator that returns combinations of elements from + * {@code iterable}. + * + * Combinations are obtained by taking the {@see goog.iter#permutations} of + * {@code iterable} and filtering those whose elements appear in the order they + * are encountered in {@code iterable}. For example, the 3-length combinations + * of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}. + * @see http://docs.python.org/2/library/itertools.html#itertools.combinations + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable from which to generate combinations. + * @param {number} length The length of each combination. + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing + * combinations from the {@code iterable}. + * @template VALUE + */ +goog.iter.combinations = function(iterable, length) { + var elements = goog.iter.toArray(iterable); + var indexes = goog.iter.range(elements.length); + var indexIterator = goog.iter.permutations(indexes, length); + // sortedIndexIterator will now give arrays of with the given length that + // indicate what indexes into "elements" should be returned on each iteration. + var sortedIndexIterator = goog.iter.filter( + indexIterator, function(arr) { return goog.array.isSorted(arr); }); + + var iter = new goog.iter.Iterator(); + + function getIndexFromElements(index) { return elements[index]; } + + iter.next = function() { + return goog.array.map(sortedIndexIterator.next(), getIndexFromElements); + }; + + return iter; +}; + + +/** + * Creates an iterator that returns combinations of elements from + * {@code iterable}, with repeated elements possible. + * + * Combinations are obtained by taking the Cartesian product of {@code length} + * iterables and filtering those whose elements appear in the order they are + * encountered in {@code iterable}. For example, the 2-length combinations of + * {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}. + * @see https://goo.gl/C0yXe4 + * @see https://goo.gl/djOCsk + * @param {!goog.iter.Iterator<VALUE>|!goog.iter.Iterable} iterable The + * iterable to combine. + * @param {number} length The length of each combination. + * @return {!goog.iter.Iterator<!Array<VALUE>>} A new iterator containing + * combinations from the {@code iterable}. + * @template VALUE + */ +goog.iter.combinationsWithReplacement = function(iterable, length) { + var elements = goog.iter.toArray(iterable); + var indexes = goog.array.range(elements.length); + var sets = goog.array.repeat(indexes, length); + var indexIterator = goog.iter.product.apply(undefined, sets); + // sortedIndexIterator will now give arrays of with the given length that + // indicate what indexes into "elements" should be returned on each iteration. + var sortedIndexIterator = goog.iter.filter( + indexIterator, function(arr) { return goog.array.isSorted(arr); }); + + var iter = new goog.iter.Iterator(); + + function getIndexFromElements(index) { return elements[index]; } + + iter.next = function() { + return goog.array.map( + /** @type {!Array<number>} */ + (sortedIndexIterator.next()), getIndexFromElements); + }; + + return iter; +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Datastructure: Hash Map. + * + * @author arv@google.com (Erik Arvidsson) + * + * This file contains an implementation of a Map structure. It implements a lot + * of the methods used in goog.structs so those functions work on hashes. This + * is best suited for complex key types. For simple keys such as numbers and + * strings consider using the lighter-weight utilities in goog.object. + */ + + +goog.provide('goog.structs.Map'); + +goog.require('goog.iter.Iterator'); +goog.require('goog.iter.StopIteration'); +goog.require('goog.object'); + + + +/** + * Class for Hash Map datastructure. + * @param {*=} opt_map Map or Object to initialize the map with. + * @param {...*} var_args If 2 or more arguments are present then they + * will be used as key-value pairs. + * @constructor + * @template K, V + */ +goog.structs.Map = function(opt_map, var_args) { + + /** + * Underlying JS object used to implement the map. + * @private {!Object} + */ + this.map_ = {}; + + /** + * An array of keys. This is necessary for two reasons: + * 1. Iterating the keys using for (var key in this.map_) allocates an + * object for every key in IE which is really bad for IE6 GC perf. + * 2. Without a side data structure, we would need to escape all the keys + * as that would be the only way we could tell during iteration if the + * key was an internal key or a property of the object. + * + * This array can contain deleted keys so it's necessary to check the map + * as well to see if the key is still in the map (this doesn't require a + * memory allocation in IE). + * @private {!Array<string>} + */ + this.keys_ = []; + + /** + * The number of key value pairs in the map. + * @private {number} + */ + this.count_ = 0; + + /** + * Version used to detect changes while iterating. + * @private {number} + */ + this.version_ = 0; + + var argLength = arguments.length; + + if (argLength > 1) { + if (argLength % 2) { + throw Error('Uneven number of arguments'); + } + for (var i = 0; i < argLength; i += 2) { + this.set(arguments[i], arguments[i + 1]); + } + } else if (opt_map) { + this.addAll(/** @type {Object} */ (opt_map)); + } +}; + + +/** + * @return {number} The number of key-value pairs in the map. + */ +goog.structs.Map.prototype.getCount = function() { + return this.count_; +}; + + +/** + * Returns the values of the map. + * @return {!Array<V>} The values in the map. + */ +goog.structs.Map.prototype.getValues = function() { + this.cleanupKeysArray_(); + + var rv = []; + for (var i = 0; i < this.keys_.length; i++) { + var key = this.keys_[i]; + rv.push(this.map_[key]); + } + return rv; +}; + + +/** + * Returns the keys of the map. + * @return {!Array<string>} Array of string values. + */ +goog.structs.Map.prototype.getKeys = function() { + this.cleanupKeysArray_(); + return /** @type {!Array<string>} */ (this.keys_.concat()); +}; + + +/** + * Whether the map contains the given key. + * @param {*} key The key to check for. + * @return {boolean} Whether the map contains the key. + */ +goog.structs.Map.prototype.containsKey = function(key) { + return goog.structs.Map.hasKey_(this.map_, key); +}; + + +/** + * Whether the map contains the given value. This is O(n). + * @param {V} val The value to check for. + * @return {boolean} Whether the map contains the value. + */ +goog.structs.Map.prototype.containsValue = function(val) { + for (var i = 0; i < this.keys_.length; i++) { + var key = this.keys_[i]; + if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) { + return true; + } + } + return false; +}; + + +/** + * Whether this map is equal to the argument map. + * @param {goog.structs.Map} otherMap The map against which to test equality. + * @param {function(V, V): boolean=} opt_equalityFn Optional equality function + * to test equality of values. If not specified, this will test whether + * the values contained in each map are identical objects. + * @return {boolean} Whether the maps are equal. + */ +goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) { + if (this === otherMap) { + return true; + } + + if (this.count_ != otherMap.getCount()) { + return false; + } + + var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals; + + this.cleanupKeysArray_(); + for (var key, i = 0; key = this.keys_[i]; i++) { + if (!equalityFn(this.get(key), otherMap.get(key))) { + return false; + } + } + + return true; +}; + + +/** + * Default equality test for values. + * @param {*} a The first value. + * @param {*} b The second value. + * @return {boolean} Whether a and b reference the same object. + */ +goog.structs.Map.defaultEquals = function(a, b) { + return a === b; +}; + + +/** + * @return {boolean} Whether the map is empty. + */ +goog.structs.Map.prototype.isEmpty = function() { + return this.count_ == 0; +}; + + +/** + * Removes all key-value pairs from the map. + */ +goog.structs.Map.prototype.clear = function() { + this.map_ = {}; + this.keys_.length = 0; + this.count_ = 0; + this.version_ = 0; +}; + + +/** + * Removes a key-value pair based on the key. This is O(logN) amortized due to + * updating the keys array whenever the count becomes half the size of the keys + * in the keys array. + * @param {*} key The key to remove. + * @return {boolean} Whether object was removed. + */ +goog.structs.Map.prototype.remove = function(key) { + if (goog.structs.Map.hasKey_(this.map_, key)) { + delete this.map_[key]; + this.count_--; + this.version_++; + + // clean up the keys array if the threshold is hit + if (this.keys_.length > 2 * this.count_) { + this.cleanupKeysArray_(); + } + + return true; + } + return false; +}; + + +/** + * Cleans up the temp keys array by removing entries that are no longer in the + * map. + * @private + */ +goog.structs.Map.prototype.cleanupKeysArray_ = function() { + if (this.count_ != this.keys_.length) { + // First remove keys that are no longer in the map. + var srcIndex = 0; + var destIndex = 0; + while (srcIndex < this.keys_.length) { + var key = this.keys_[srcIndex]; + if (goog.structs.Map.hasKey_(this.map_, key)) { + this.keys_[destIndex++] = key; + } + srcIndex++; + } + this.keys_.length = destIndex; + } + + if (this.count_ != this.keys_.length) { + // If the count still isn't correct, that means we have duplicates. This can + // happen when the same key is added and removed multiple times. Now we have + // to allocate one extra Object to remove the duplicates. This could have + // been done in the first pass, but in the common case, we can avoid + // allocating an extra object by only doing this when necessary. + var seen = {}; + var srcIndex = 0; + var destIndex = 0; + while (srcIndex < this.keys_.length) { + var key = this.keys_[srcIndex]; + if (!(goog.structs.Map.hasKey_(seen, key))) { + this.keys_[destIndex++] = key; + seen[key] = 1; + } + srcIndex++; + } + this.keys_.length = destIndex; + } +}; + + +/** + * Returns the value for the given key. If the key is not found and the default + * value is not given this will return {@code undefined}. + * @param {*} key The key to get the value for. + * @param {DEFAULT=} opt_val The value to return if no item is found for the + * given key, defaults to undefined. + * @return {V|DEFAULT} The value for the given key. + * @template DEFAULT + */ +goog.structs.Map.prototype.get = function(key, opt_val) { + if (goog.structs.Map.hasKey_(this.map_, key)) { + return this.map_[key]; + } + return opt_val; +}; + + +/** + * Adds a key-value pair to the map. + * @param {*} key The key. + * @param {V} value The value to add. + * @return {*} Some subclasses return a value. + */ +goog.structs.Map.prototype.set = function(key, value) { + if (!(goog.structs.Map.hasKey_(this.map_, key))) { + this.count_++; + // TODO(johnlenz): This class lies, it claims to return an array of string + // keys, but instead returns the original object used. + this.keys_.push(/** @type {?} */ (key)); + // Only change the version if we add a new key. + this.version_++; + } + this.map_[key] = value; +}; + + +/** + * Adds multiple key-value pairs from another goog.structs.Map or Object. + * @param {Object} map Object containing the data to add. + */ +goog.structs.Map.prototype.addAll = function(map) { + var keys, values; + if (map instanceof goog.structs.Map) { + keys = map.getKeys(); + values = map.getValues(); + } else { + keys = goog.object.getKeys(map); + values = goog.object.getValues(map); + } + // we could use goog.array.forEach here but I don't want to introduce that + // dependency just for this. + for (var i = 0; i < keys.length; i++) { + this.set(keys[i], values[i]); + } +}; + + +/** + * Calls the given function on each entry in the map. + * @param {function(this:T, V, K, goog.structs.Map<K,V>)} f + * @param {T=} opt_obj The value of "this" inside f. + * @template T + */ +goog.structs.Map.prototype.forEach = function(f, opt_obj) { + var keys = this.getKeys(); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = this.get(key); + f.call(opt_obj, value, key, this); + } +}; + + +/** + * Clones a map and returns a new map. + * @return {!goog.structs.Map} A new map with the same key-value pairs. + */ +goog.structs.Map.prototype.clone = function() { + return new goog.structs.Map(this); +}; + + +/** + * Returns a new map in which all the keys and values are interchanged + * (keys become values and values become keys). If multiple keys map to the + * same value, the chosen transposed value is implementation-dependent. + * + * It acts very similarly to {goog.object.transpose(Object)}. + * + * @return {!goog.structs.Map} The transposed map. + */ +goog.structs.Map.prototype.transpose = function() { + var transposed = new goog.structs.Map(); + for (var i = 0; i < this.keys_.length; i++) { + var key = this.keys_[i]; + var value = this.map_[key]; + transposed.set(value, key); + } + + return transposed; +}; + + +/** + * @return {!Object} Object representation of the map. + */ +goog.structs.Map.prototype.toObject = function() { + this.cleanupKeysArray_(); + var obj = {}; + for (var i = 0; i < this.keys_.length; i++) { + var key = this.keys_[i]; + obj[key] = this.map_[key]; + } + return obj; +}; + + +/** + * Returns an iterator that iterates over the keys in the map. Removal of keys + * while iterating might have undesired side effects. + * @return {!goog.iter.Iterator} An iterator over the keys in the map. + */ +goog.structs.Map.prototype.getKeyIterator = function() { + return this.__iterator__(true); +}; + + +/** + * Returns an iterator that iterates over the values in the map. Removal of + * keys while iterating might have undesired side effects. + * @return {!goog.iter.Iterator} An iterator over the values in the map. + */ +goog.structs.Map.prototype.getValueIterator = function() { + return this.__iterator__(false); +}; + + +/** + * Returns an iterator that iterates over the values or the keys in the map. + * This throws an exception if the map was mutated since the iterator was + * created. + * @param {boolean=} opt_keys True to iterate over the keys. False to iterate + * over the values. The default value is false. + * @return {!goog.iter.Iterator} An iterator over the values or keys in the map. + */ +goog.structs.Map.prototype.__iterator__ = function(opt_keys) { + // Clean up keys to minimize the risk of iterating over dead keys. + this.cleanupKeysArray_(); + + var i = 0; + var version = this.version_; + var selfObj = this; + + var newIter = new goog.iter.Iterator; + newIter.next = function() { + if (version != selfObj.version_) { + throw Error('The map has changed since the iterator was created'); + } + if (i >= selfObj.keys_.length) { + throw goog.iter.StopIteration; + } + var key = selfObj.keys_[i++]; + return opt_keys ? key : selfObj.map_[key]; + }; + return newIter; +}; + + +/** + * Safe way to test for hasOwnProperty. It even allows testing for + * 'hasOwnProperty'. + * @param {Object} obj The object to test for presence of the given key. + * @param {*} key The key to check for. + * @return {boolean} Whether the object has the key. + * @private + */ +goog.structs.Map.hasKey_ = function(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); +}; + +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Simple utilities for dealing with URI strings. + * + * This is intended to be a lightweight alternative to constructing goog.Uri + * objects. Whereas goog.Uri adds several kilobytes to the binary regardless + * of how much of its functionality you use, this is designed to be a set of + * mostly-independent utilities so that the compiler includes only what is + * necessary for the task. Estimated savings of porting is 5k pre-gzip and + * 1.5k post-gzip. To ensure the savings remain, future developers should + * avoid adding new functionality to existing functions, but instead create + * new ones and factor out shared code. + * + * Many of these utilities have limited functionality, tailored to common + * cases. The query parameter utilities assume that the parameter keys are + * already encoded, since most keys are compile-time alphanumeric strings. The + * query parameter mutation utilities also do not tolerate fragment identifiers. + * + * By design, these functions can be slower than goog.Uri equivalents. + * Repeated calls to some of functions may be quadratic in behavior for IE, + * although the effect is somewhat limited given the 2kb limit. + * + * One advantage of the limited functionality here is that this approach is + * less sensitive to differences in URI encodings than goog.Uri, since these + * functions operate on strings directly, rather than decoding them and + * then re-encoding. + * + * Uses features of RFC 3986 for parsing/formatting URIs: + * http://www.ietf.org/rfc/rfc3986.txt + * + * @author gboyer@google.com (Garrett Boyer) - The "lightened" design. + */ + +goog.provide('goog.uri.utils'); +goog.provide('goog.uri.utils.ComponentIndex'); +goog.provide('goog.uri.utils.QueryArray'); +goog.provide('goog.uri.utils.QueryValue'); +goog.provide('goog.uri.utils.StandardQueryParam'); + +goog.require('goog.asserts'); +goog.require('goog.string'); + + +/** + * Character codes inlined to avoid object allocations due to charCode. + * @enum {number} + * @private + */ +goog.uri.utils.CharCode_ = { + AMPERSAND: 38, + EQUAL: 61, + HASH: 35, + QUESTION: 63 +}; + + +/** + * Builds a URI string from already-encoded parts. + * + * No encoding is performed. Any component may be omitted as either null or + * undefined. + * + * @param {?string=} opt_scheme The scheme such as 'http'. + * @param {?string=} opt_userInfo The user name before the '@'. + * @param {?string=} opt_domain The domain such as 'www.google.com', already + * URI-encoded. + * @param {(string|number|null)=} opt_port The port number. + * @param {?string=} opt_path The path, already URI-encoded. If it is not + * empty, it must begin with a slash. + * @param {?string=} opt_queryData The URI-encoded query data. + * @param {?string=} opt_fragment The URI-encoded fragment identifier. + * @return {string} The fully combined URI. + */ +goog.uri.utils.buildFromEncodedParts = function( + opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_queryData, + opt_fragment) { + var out = ''; + + if (opt_scheme) { + out += opt_scheme + ':'; + } + + if (opt_domain) { + out += '//'; + + if (opt_userInfo) { + out += opt_userInfo + '@'; + } + + out += opt_domain; + + if (opt_port) { + out += ':' + opt_port; + } + } + + if (opt_path) { + out += opt_path; + } + + if (opt_queryData) { + out += '?' + opt_queryData; + } + + if (opt_fragment) { + out += '#' + opt_fragment; + } + + return out; +}; + + +/** + * A regular expression for breaking a URI into its component parts. + * + * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B + * As the "first-match-wins" algorithm is identical to the "greedy" + * disambiguation method used by POSIX regular expressions, it is natural and + * commonplace to use a regular expression for parsing the potential five + * components of a URI reference. + * + * The following line is the regular expression for breaking-down a + * well-formed URI reference into its components. + * + * <pre> + * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? + * 12 3 4 5 6 7 8 9 + * </pre> + * + * The numbers in the second line above are only to assist readability; they + * indicate the reference points for each subexpression (i.e., each paired + * parenthesis). We refer to the value matched for subexpression <n> as $<n>. + * For example, matching the above expression to + * <pre> + * http://www.ics.uci.edu/pub/ietf/uri/#Related + * </pre> + * results in the following subexpression matches: + * <pre> + * $1 = http: + * $2 = http + * $3 = //www.ics.uci.edu + * $4 = www.ics.uci.edu + * $5 = /pub/ietf/uri/ + * $6 = <undefined> + * $7 = <undefined> + * $8 = #Related + * $9 = Related + * </pre> + * where <undefined> indicates that the component is not present, as is the + * case for the query component in the above example. Therefore, we can + * determine the value of the five components as + * <pre> + * scheme = $2 + * authority = $4 + * path = $5 + * query = $7 + * fragment = $9 + * </pre> + * + * The regular expression has been modified slightly to expose the + * userInfo, domain, and port separately from the authority. + * The modified version yields + * <pre> + * $1 = http scheme + * $2 = <undefined> userInfo -\ + * $3 = www.ics.uci.edu domain | authority + * $4 = <undefined> port -/ + * $5 = /pub/ietf/uri/ path + * $6 = <undefined> query without ? + * $7 = Related fragment without # + * </pre> + * @type {!RegExp} + * @private + */ +goog.uri.utils.splitRe_ = new RegExp( + '^' + + '(?:' + + '([^:/?#.]+)' + // scheme - ignore special characters + // used by other URL parts such as :, + // ?, /, #, and . + ':)?' + + '(?://' + + '(?:([^/?#]*)@)?' + // userInfo + '([^/#?]*?)' + // domain + '(?::([0-9]+))?' + // port + '(?=[/#?]|$)' + // authority-terminating character + ')?' + + '([^?#]+)?' + // path + '(?:\\?([^#]*))?' + // query + '(?:#(.*))?' + // fragment + '$'); + + +/** + * The index of each URI component in the return value of goog.uri.utils.split. + * @enum {number} + */ +goog.uri.utils.ComponentIndex = { + SCHEME: 1, + USER_INFO: 2, + DOMAIN: 3, + PORT: 4, + PATH: 5, + QUERY_DATA: 6, + FRAGMENT: 7 +}; + + +/** + * Splits a URI into its component parts. + * + * Each component can be accessed via the component indices; for example: + * <pre> + * goog.uri.utils.split(someStr)[goog.uri.utils.CompontentIndex.QUERY_DATA]; + * </pre> + * + * @param {string} uri The URI string to examine. + * @return {!Array<string|undefined>} Each component still URI-encoded. + * Each component that is present will contain the encoded value, whereas + * components that are not present will be undefined or empty, depending + * on the browser's regular expression implementation. Never null, since + * arbitrary strings may still look like path names. + */ +goog.uri.utils.split = function(uri) { + // See @return comment -- never null. + return /** @type {!Array<string|undefined>} */ ( + uri.match(goog.uri.utils.splitRe_)); +}; + + +/** + * @param {?string} uri A possibly null string. + * @param {boolean=} opt_preserveReserved If true, percent-encoding of RFC-3986 + * reserved characters will not be removed. + * @return {?string} The string URI-decoded, or null if uri is null. + * @private + */ +goog.uri.utils.decodeIfPossible_ = function(uri, opt_preserveReserved) { + if (!uri) { + return uri; + } + + return opt_preserveReserved ? decodeURI(uri) : decodeURIComponent(uri); +}; + + +/** + * Gets a URI component by index. + * + * It is preferred to use the getPathEncoded() variety of functions ahead, + * since they are more readable. + * + * @param {goog.uri.utils.ComponentIndex} componentIndex The component index. + * @param {string} uri The URI to examine. + * @return {?string} The still-encoded component, or null if the component + * is not present. + * @private + */ +goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) { + // Convert undefined, null, and empty string into null. + return goog.uri.utils.split(uri)[componentIndex] || null; +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The protocol or scheme, or null if none. Does not + * include trailing colons or slashes. + */ +goog.uri.utils.getScheme = function(uri) { + return goog.uri.utils.getComponentByIndex_( + goog.uri.utils.ComponentIndex.SCHEME, uri); +}; + + +/** + * Gets the effective scheme for the URL. If the URL is relative then the + * scheme is derived from the page's location. + * @param {string} uri The URI to examine. + * @return {string} The protocol or scheme, always lower case. + */ +goog.uri.utils.getEffectiveScheme = function(uri) { + var scheme = goog.uri.utils.getScheme(uri); + if (!scheme && goog.global.self && goog.global.self.location) { + var protocol = goog.global.self.location.protocol; + scheme = protocol.substr(0, protocol.length - 1); + } + // NOTE: When called from a web worker in Firefox 3.5, location maybe null. + // All other browsers with web workers support self.location from the worker. + return scheme ? scheme.toLowerCase() : ''; +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The user name still encoded, or null if none. + */ +goog.uri.utils.getUserInfoEncoded = function(uri) { + return goog.uri.utils.getComponentByIndex_( + goog.uri.utils.ComponentIndex.USER_INFO, uri); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The decoded user info, or null if none. + */ +goog.uri.utils.getUserInfo = function(uri) { + return goog.uri.utils.decodeIfPossible_( + goog.uri.utils.getUserInfoEncoded(uri)); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The domain name still encoded, or null if none. + */ +goog.uri.utils.getDomainEncoded = function(uri) { + return goog.uri.utils.getComponentByIndex_( + goog.uri.utils.ComponentIndex.DOMAIN, uri); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The decoded domain, or null if none. + */ +goog.uri.utils.getDomain = function(uri) { + return goog.uri.utils.decodeIfPossible_( + goog.uri.utils.getDomainEncoded(uri), true /* opt_preserveReserved */); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?number} The port number, or null if none. + */ +goog.uri.utils.getPort = function(uri) { + // Coerce to a number. If the result of getComponentByIndex_ is null or + // non-numeric, the number coersion yields NaN. This will then return + // null for all non-numeric cases (though also zero, which isn't a relevant + // port number). + return Number( + goog.uri.utils.getComponentByIndex_( + goog.uri.utils.ComponentIndex.PORT, uri)) || + null; +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The path still encoded, or null if none. Includes the + * leading slash, if any. + */ +goog.uri.utils.getPathEncoded = function(uri) { + return goog.uri.utils.getComponentByIndex_( + goog.uri.utils.ComponentIndex.PATH, uri); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The decoded path, or null if none. Includes the leading + * slash, if any. + */ +goog.uri.utils.getPath = function(uri) { + return goog.uri.utils.decodeIfPossible_( + goog.uri.utils.getPathEncoded(uri), true /* opt_preserveReserved */); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The query data still encoded, or null if none. Does not + * include the question mark itself. + */ +goog.uri.utils.getQueryData = function(uri) { + return goog.uri.utils.getComponentByIndex_( + goog.uri.utils.ComponentIndex.QUERY_DATA, uri); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The fragment identifier, or null if none. Does not + * include the hash mark itself. + */ +goog.uri.utils.getFragmentEncoded = function(uri) { + // The hash mark may not appear in any other part of the URL. + var hashIndex = uri.indexOf('#'); + return hashIndex < 0 ? null : uri.substr(hashIndex + 1); +}; + + +/** + * @param {string} uri The URI to examine. + * @param {?string} fragment The encoded fragment identifier, or null if none. + * Does not include the hash mark itself. + * @return {string} The URI with the fragment set. + */ +goog.uri.utils.setFragmentEncoded = function(uri, fragment) { + return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : ''); +}; + + +/** + * @param {string} uri The URI to examine. + * @return {?string} The decoded fragment identifier, or null if none. Does + * not include the hash mark. + */ +goog.uri.utils.getFragment = function(uri) { + return goog.uri.utils.decodeIfPossible_( + goog.uri.utils.getFragmentEncoded(uri)); +}; + + +/** + * Extracts everything up to the port of the URI. + * @param {string} uri The URI string. + * @return {string} Everything up to and including the port. + */ +goog.uri.utils.getHost = function(uri) { + var pieces = goog.uri.utils.split(uri); + return goog.uri.utils.buildFromEncodedParts( + pieces[goog.uri.utils.ComponentIndex.SCHEME], + pieces[goog.uri.utils.ComponentIndex.USER_INFO], + pieces[goog.uri.utils.ComponentIndex.DOMAIN], + pieces[goog.uri.utils.ComponentIndex.PORT]); +}; + + +/** + * Extracts the path of the URL and everything after. + * @param {string} uri The URI string. + * @return {string} The URI, starting at the path and including the query + * parameters and fragment identifier. + */ +goog.uri.utils.getPathAndAfter = function(uri) { + var pieces = goog.uri.utils.split(uri); + return goog.uri.utils.buildFromEncodedParts( + null, null, null, null, pieces[goog.uri.utils.ComponentIndex.PATH], + pieces[goog.uri.utils.ComponentIndex.QUERY_DATA], + pieces[goog.uri.utils.ComponentIndex.FRAGMENT]); +}; + + +/** + * Gets the URI with the fragment identifier removed. + * @param {string} uri The URI to examine. + * @return {string} Everything preceding the hash mark. + */ +goog.uri.utils.removeFragment = function(uri) { + // The hash mark may not appear in any other part of the URL. + var hashIndex = uri.indexOf('#'); + return hashIndex < 0 ? uri : uri.substr(0, hashIndex); +}; + + +/** + * Ensures that two URI's have the exact same domain, scheme, and port. + * + * Unlike the version in goog.Uri, this checks protocol, and therefore is + * suitable for checking against the browser's same-origin policy. + * + * @param {string} uri1 The first URI. + * @param {string} uri2 The second URI. + * @return {boolean} Whether they have the same scheme, domain and port. + */ +goog.uri.utils.haveSameDomain = function(uri1, uri2) { + var pieces1 = goog.uri.utils.split(uri1); + var pieces2 = goog.uri.utils.split(uri2); + return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] == + pieces2[goog.uri.utils.ComponentIndex.DOMAIN] && + pieces1[goog.uri.utils.ComponentIndex.SCHEME] == + pieces2[goog.uri.utils.ComponentIndex.SCHEME] && + pieces1[goog.uri.utils.ComponentIndex.PORT] == + pieces2[goog.uri.utils.ComponentIndex.PORT]; +}; + + +/** + * Asserts that there are no fragment or query identifiers, only in uncompiled + * mode. + * @param {string} uri The URI to examine. + * @private + */ +goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) { + // NOTE: would use goog.asserts here, but jscompiler doesn't know that + // indexOf has no side effects. + if (goog.DEBUG && (uri.indexOf('#') >= 0 || uri.indexOf('?') >= 0)) { + throw Error( + 'goog.uri.utils: Fragment or query identifiers are not ' + + 'supported: [' + uri + ']'); + } +}; + + +/** + * Supported query parameter values by the parameter serializing utilities. + * + * If a value is null or undefined, the key-value pair is skipped, as an easy + * way to omit parameters conditionally. Non-array parameters are converted + * to a string and URI encoded. Array values are expanded into multiple + * &key=value pairs, with each element stringized and URI-encoded. + * + * @typedef {*} + */ +goog.uri.utils.QueryValue; + + +/** + * An array representing a set of query parameters with alternating keys + * and values. + * + * Keys are assumed to be URI encoded already and live at even indices. See + * goog.uri.utils.QueryValue for details on how parameter values are encoded. + * + * Example: + * <pre> + * var data = [ + * // Simple param: ?name=BobBarker + * 'name', 'BobBarker', + * // Conditional param -- may be omitted entirely. + * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null, + * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null + * 'house', ['LosAngeles', 'NewYork', null] + * ]; + * </pre> + * + * @typedef {!Array<string|goog.uri.utils.QueryValue>} + */ +goog.uri.utils.QueryArray; + + +/** + * Parses encoded query parameters and calls callback function for every + * parameter found in the string. + * + * Missing value of parameter (e.g. “…&key&…â€) is treated as if the value was an + * empty string. Keys may be empty strings (e.g. “…&=value&…â€) which also means + * that “…&=&…†and “…&&…†will result in an empty key and value. + * + * @param {string} encodedQuery Encoded query string excluding question mark at + * the beginning. + * @param {function(string, string)} callback Function called for every + * parameter found in query string. The first argument (name) will not be + * urldecoded (so the function is consistent with buildQueryData), but the + * second will. If the parameter has no value (i.e. “=†was not present) + * the second argument (value) will be an empty string. + */ +goog.uri.utils.parseQueryData = function(encodedQuery, callback) { + if (!encodedQuery) { + return; + } + var pairs = encodedQuery.split('&'); + for (var i = 0; i < pairs.length; i++) { + var indexOfEquals = pairs[i].indexOf('='); + var name = null; + var value = null; + if (indexOfEquals >= 0) { + name = pairs[i].substring(0, indexOfEquals); + value = pairs[i].substring(indexOfEquals + 1); + } else { + name = pairs[i]; + } + callback(name, value ? goog.string.urlDecode(value) : ''); + } +}; + + +/** + * Appends a URI and query data in a string buffer with special preconditions. + * + * Internal implementation utility, performing very few object allocations. + * + * @param {!Array<string|undefined>} buffer A string buffer. The first element + * must be the base URI, and may have a fragment identifier. If the array + * contains more than one element, the second element must be an ampersand, + * and may be overwritten, depending on the base URI. Undefined elements + * are treated as empty-string. + * @return {string} The concatenated URI and query data. + * @private + */ +goog.uri.utils.appendQueryData_ = function(buffer) { + if (buffer[1]) { + // At least one query parameter was added. We need to check the + // punctuation mark, which is currently an ampersand, and also make sure + // there aren't any interfering fragment identifiers. + var baseUri = /** @type {string} */ (buffer[0]); + var hashIndex = baseUri.indexOf('#'); + if (hashIndex >= 0) { + // Move the fragment off the base part of the URI into the end. + buffer.push(baseUri.substr(hashIndex)); + buffer[0] = baseUri = baseUri.substr(0, hashIndex); + } + var questionIndex = baseUri.indexOf('?'); + if (questionIndex < 0) { + // No question mark, so we need a question mark instead of an ampersand. + buffer[1] = '?'; + } else if (questionIndex == baseUri.length - 1) { + // Question mark is the very last character of the existing URI, so don't + // append an additional delimiter. + buffer[1] = undefined; + } + } + + return buffer.join(''); +}; + + +/** + * Appends key=value pairs to an array, supporting multi-valued objects. + * @param {string} key The key prefix. + * @param {goog.uri.utils.QueryValue} value The value to serialize. + * @param {!Array<string>} pairs The array to which the 'key=value' strings + * should be appended. + * @private + */ +goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) { + if (goog.isArray(value)) { + // Convince the compiler it's an array. + goog.asserts.assertArray(value); + for (var j = 0; j < value.length; j++) { + // Convert to string explicitly, to short circuit the null and array + // logic in this function -- this ensures that null and undefined get + // written as literal 'null' and 'undefined', and arrays don't get + // expanded out but instead encoded in the default way. + goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs); + } + } else if (value != null) { + // Skip a top-level null or undefined entirely. + pairs.push( + '&', key, + // Check for empty string. Zero gets encoded into the url as literal + // strings. For empty string, skip the equal sign, to be consistent + // with UriBuilder.java. + value === '' ? '' : '=', goog.string.urlEncode(value)); + } +}; + + +/** + * Builds a buffer of query data from a sequence of alternating keys and values. + * + * @param {!Array<string|undefined>} buffer A string buffer to append to. The + * first element appended will be an '&', and may be replaced by the caller. + * @param {!goog.uri.utils.QueryArray|!Arguments} keysAndValues An array with + * alternating keys and values -- see the typedef. + * @param {number=} opt_startIndex A start offset into the arary, defaults to 0. + * @return {!Array<string|undefined>} The buffer argument. + * @private + */ +goog.uri.utils.buildQueryDataBuffer_ = function( + buffer, keysAndValues, opt_startIndex) { + goog.asserts.assert( + Math.max(keysAndValues.length - (opt_startIndex || 0), 0) % 2 == 0, + 'goog.uri.utils: Key/value lists must be even in length.'); + + for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) { + goog.uri.utils.appendKeyValuePairs_( + keysAndValues[i], keysAndValues[i + 1], buffer); + } + + return buffer; +}; + + +/** + * Builds a query data string from a sequence of alternating keys and values. + * Currently generates "&key&" for empty args. + * + * @param {goog.uri.utils.QueryArray} keysAndValues Alternating keys and + * values. See the typedef. + * @param {number=} opt_startIndex A start offset into the arary, defaults to 0. + * @return {string} The encoded query string, in the form 'a=1&b=2'. + */ +goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) { + var buffer = + goog.uri.utils.buildQueryDataBuffer_([], keysAndValues, opt_startIndex); + buffer[0] = ''; // Remove the leading ampersand. + return buffer.join(''); +}; + + +/** + * Builds a buffer of query data from a map. + * + * @param {!Array<string|undefined>} buffer A string buffer to append to. The + * first element appended will be an '&', and may be replaced by the caller. + * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys + * are URI-encoded parameter keys, and the values conform to the contract + * specified in the goog.uri.utils.QueryValue typedef. + * @return {!Array<string|undefined>} The buffer argument. + * @private + */ +goog.uri.utils.buildQueryDataBufferFromMap_ = function(buffer, map) { + for (var key in map) { + goog.uri.utils.appendKeyValuePairs_(key, map[key], buffer); + } + + return buffer; +}; + + +/** + * Builds a query data string from a map. + * Currently generates "&key&" for empty args. + * + * @param {!Object<string, goog.uri.utils.QueryValue>} map An object where keys + * are URI-encoded parameter keys, and the values are arbitrary types + * or arrays. Keys with a null value are dropped. + * @return {string} The encoded query string, in the form 'a=1&b=2'. + */ +goog.uri.utils.buildQueryDataFromMap = function(map) { + var buffer = goog.uri.utils.buildQueryDataBufferFromMap_([], map); + buffer[0] = ''; + return buffer.join(''); +}; + + +/** + * Appends URI parameters to an existing URI. + * + * The variable arguments may contain alternating keys and values. Keys are + * assumed to be already URI encoded. The values should not be URI-encoded, + * and will instead be encoded by this function. + * <pre> + * appendParams('http://www.foo.com?existing=true', + * 'key1', 'value1', + * 'key2', 'value?willBeEncoded', + * 'key3', ['valueA', 'valueB', 'valueC'], + * 'key4', null); + * result: 'http://www.foo.com?existing=true&' + + * 'key1=value1&' + + * 'key2=value%3FwillBeEncoded&' + + * 'key3=valueA&key3=valueB&key3=valueC' + * </pre> + * + * A single call to this function will not exhibit quadratic behavior in IE, + * whereas multiple repeated calls may, although the effect is limited by + * fact that URL's generally can't exceed 2kb. + * + * @param {string} uri The original URI, which may already have query data. + * @param {...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)} + * var_args + * An array or argument list conforming to goog.uri.utils.QueryArray. + * @return {string} The URI with all query parameters added. + */ +goog.uri.utils.appendParams = function(uri, var_args) { + return goog.uri.utils.appendQueryData_( + arguments.length == 2 ? + goog.uri.utils.buildQueryDataBuffer_([uri], arguments[1], 0) : + goog.uri.utils.buildQueryDataBuffer_([uri], arguments, 1)); +}; + + +/** + * Appends query parameters from a map. + * + * @param {string} uri The original URI, which may already have query data. + * @param {!Object<goog.uri.utils.QueryValue>} map An object where keys are + * URI-encoded parameter keys, and the values are arbitrary types or arrays. + * Keys with a null value are dropped. + * @return {string} The new parameters. + */ +goog.uri.utils.appendParamsFromMap = function(uri, map) { + return goog.uri.utils.appendQueryData_( + goog.uri.utils.buildQueryDataBufferFromMap_([uri], map)); +}; + + +/** + * Appends a single URI parameter. + * + * Repeated calls to this can exhibit quadratic behavior in IE6 due to the + * way string append works, though it should be limited given the 2kb limit. + * + * @param {string} uri The original URI, which may already have query data. + * @param {string} key The key, which must already be URI encoded. + * @param {*=} opt_value The value, which will be stringized and encoded + * (assumed not already to be encoded). If omitted, undefined, or null, the + * key will be added as a valueless parameter. + * @return {string} The URI with the query parameter added. + */ +goog.uri.utils.appendParam = function(uri, key, opt_value) { + var paramArr = [uri, '&', key]; + if (goog.isDefAndNotNull(opt_value)) { + paramArr.push('=', goog.string.urlEncode(opt_value)); + } + return goog.uri.utils.appendQueryData_(paramArr); +}; + + +/** + * Finds the next instance of a query parameter with the specified name. + * + * Does not instantiate any objects. + * + * @param {string} uri The URI to search. May contain a fragment identifier + * if opt_hashIndex is specified. + * @param {number} startIndex The index to begin searching for the key at. A + * match may be found even if this is one character after the ampersand. + * @param {string} keyEncoded The URI-encoded key. + * @param {number} hashOrEndIndex Index to stop looking at. If a hash + * mark is present, it should be its index, otherwise it should be the + * length of the string. + * @return {number} The position of the first character in the key's name, + * immediately after either a question mark or a dot. + * @private + */ +goog.uri.utils.findParam_ = function( + uri, startIndex, keyEncoded, hashOrEndIndex) { + var index = startIndex; + var keyLength = keyEncoded.length; + + // Search for the key itself and post-filter for surronuding punctuation, + // rather than expensively building a regexp. + while ((index = uri.indexOf(keyEncoded, index)) >= 0 && + index < hashOrEndIndex) { + var precedingChar = uri.charCodeAt(index - 1); + // Ensure that the preceding character is '&' or '?'. + if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND || + precedingChar == goog.uri.utils.CharCode_.QUESTION) { + // Ensure the following character is '&', '=', '#', or NaN + // (end of string). + var followingChar = uri.charCodeAt(index + keyLength); + if (!followingChar || followingChar == goog.uri.utils.CharCode_.EQUAL || + followingChar == goog.uri.utils.CharCode_.AMPERSAND || + followingChar == goog.uri.utils.CharCode_.HASH) { + return index; + } + } + index += keyLength + 1; + } + + return -1; +}; + + +/** + * Regular expression for finding a hash mark or end of string. + * @type {RegExp} + * @private + */ +goog.uri.utils.hashOrEndRe_ = /#|$/; + + +/** + * Determines if the URI contains a specific key. + * + * Performs no object instantiations. + * + * @param {string} uri The URI to process. May contain a fragment + * identifier. + * @param {string} keyEncoded The URI-encoded key. Case-sensitive. + * @return {boolean} Whether the key is present. + */ +goog.uri.utils.hasParam = function(uri, keyEncoded) { + return goog.uri.utils.findParam_( + uri, 0, keyEncoded, uri.search(goog.uri.utils.hashOrEndRe_)) >= 0; +}; + + +/** + * Gets the first value of a query parameter. + * @param {string} uri The URI to process. May contain a fragment. + * @param {string} keyEncoded The URI-encoded key. Case-sensitive. + * @return {?string} The first value of the parameter (URI-decoded), or null + * if the parameter is not found. + */ +goog.uri.utils.getParamValue = function(uri, keyEncoded) { + var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_); + var foundIndex = + goog.uri.utils.findParam_(uri, 0, keyEncoded, hashOrEndIndex); + + if (foundIndex < 0) { + return null; + } else { + var endPosition = uri.indexOf('&', foundIndex); + if (endPosition < 0 || endPosition > hashOrEndIndex) { + endPosition = hashOrEndIndex; + } + // Progress forth to the end of the "key=" or "key&" substring. + foundIndex += keyEncoded.length + 1; + // Use substr, because it (unlike substring) will return empty string + // if foundIndex > endPosition. + return goog.string.urlDecode( + uri.substr(foundIndex, endPosition - foundIndex)); + } +}; + + +/** + * Gets all values of a query parameter. + * @param {string} uri The URI to process. May contain a fragment. + * @param {string} keyEncoded The URI-encoded key. Case-sensitive. + * @return {!Array<string>} All URI-decoded values with the given key. + * If the key is not found, this will have length 0, but never be null. + */ +goog.uri.utils.getParamValues = function(uri, keyEncoded) { + var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_); + var position = 0; + var foundIndex; + var result = []; + + while ((foundIndex = goog.uri.utils.findParam_( + uri, position, keyEncoded, hashOrEndIndex)) >= 0) { + // Find where this parameter ends, either the '&' or the end of the + // query parameters. + position = uri.indexOf('&', foundIndex); + if (position < 0 || position > hashOrEndIndex) { + position = hashOrEndIndex; + } + + // Progress forth to the end of the "key=" or "key&" substring. + foundIndex += keyEncoded.length + 1; + // Use substr, because it (unlike substring) will return empty string + // if foundIndex > position. + result.push( + goog.string.urlDecode(uri.substr(foundIndex, position - foundIndex))); + } + + return result; +}; + + +/** + * Regexp to find trailing question marks and ampersands. + * @type {RegExp} + * @private + */ +goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/; + + +/** + * Removes all instances of a query parameter. + * @param {string} uri The URI to process. Must not contain a fragment. + * @param {string} keyEncoded The URI-encoded key. + * @return {string} The URI with all instances of the parameter removed. + */ +goog.uri.utils.removeParam = function(uri, keyEncoded) { + var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_); + var position = 0; + var foundIndex; + var buffer = []; + + // Look for a query parameter. + while ((foundIndex = goog.uri.utils.findParam_( + uri, position, keyEncoded, hashOrEndIndex)) >= 0) { + // Get the portion of the query string up to, but not including, the ? + // or & starting the parameter. + buffer.push(uri.substring(position, foundIndex)); + // Progress to immediately after the '&'. If not found, go to the end. + // Avoid including the hash mark. + position = Math.min( + (uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex, hashOrEndIndex); + } + + // Append everything that is remaining. + buffer.push(uri.substr(position)); + + // Join the buffer, and remove trailing punctuation that remains. + return buffer.join('').replace( + goog.uri.utils.trailingQueryPunctuationRe_, '$1'); +}; + + +/** + * Replaces all existing definitions of a parameter with a single definition. + * + * Repeated calls to this can exhibit quadratic behavior due to the need to + * find existing instances and reconstruct the string, though it should be + * limited given the 2kb limit. Consider using appendParams to append multiple + * parameters in bulk. + * + * @param {string} uri The original URI, which may already have query data. + * @param {string} keyEncoded The key, which must already be URI encoded. + * @param {*} value The value, which will be stringized and encoded (assumed + * not already to be encoded). + * @return {string} The URI with the query parameter added. + */ +goog.uri.utils.setParam = function(uri, keyEncoded, value) { + return goog.uri.utils.appendParam( + goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value); +}; + + +/** + * Generates a URI path using a given URI and a path with checks to + * prevent consecutive "//". The baseUri passed in must not contain + * query or fragment identifiers. The path to append may not contain query or + * fragment identifiers. + * + * @param {string} baseUri URI to use as the base. + * @param {string} path Path to append. + * @return {string} Updated URI. + */ +goog.uri.utils.appendPath = function(baseUri, path) { + goog.uri.utils.assertNoFragmentsOrQueries_(baseUri); + + // Remove any trailing '/' + if (goog.string.endsWith(baseUri, '/')) { + baseUri = baseUri.substr(0, baseUri.length - 1); + } + // Remove any leading '/' + if (goog.string.startsWith(path, '/')) { + path = path.substr(1); + } + return goog.string.buildString(baseUri, '/', path); +}; + + +/** + * Replaces the path. + * @param {string} uri URI to use as the base. + * @param {string} path New path. + * @return {string} Updated URI. + */ +goog.uri.utils.setPath = function(uri, path) { + // Add any missing '/'. + if (!goog.string.startsWith(path, '/')) { + path = '/' + path; + } + var parts = goog.uri.utils.split(uri); + return goog.uri.utils.buildFromEncodedParts( + parts[goog.uri.utils.ComponentIndex.SCHEME], + parts[goog.uri.utils.ComponentIndex.USER_INFO], + parts[goog.uri.utils.ComponentIndex.DOMAIN], + parts[goog.uri.utils.ComponentIndex.PORT], path, + parts[goog.uri.utils.ComponentIndex.QUERY_DATA], + parts[goog.uri.utils.ComponentIndex.FRAGMENT]); +}; + + +/** + * Standard supported query parameters. + * @enum {string} + */ +goog.uri.utils.StandardQueryParam = { + + /** Unused parameter for unique-ifying. */ + RANDOM: 'zx' +}; + + +/** + * Sets the zx parameter of a URI to a random value. + * @param {string} uri Any URI. + * @return {string} That URI with the "zx" parameter added or replaced to + * contain a random string. + */ +goog.uri.utils.makeUnique = function(uri) { + return goog.uri.utils.setParam( + uri, goog.uri.utils.StandardQueryParam.RANDOM, + goog.string.getRandomString()); +}; + +// Copyright 2006 The Closure Library Authors. All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/** + * @fileoverview Class for parsing and formatting URIs. + * + * Use goog.Uri(string) to parse a URI string. Use goog.Uri.create(...) to + * create a new instance of the goog.Uri object from Uri parts. + * + * e.g: <code>var myUri = new goog.Uri(window.location);</code> + * + * Implements RFC 3986 for parsing/formatting URIs. + * http://www.ietf.org/rfc/rfc3986.txt + * + * Some changes have been made to the interface (more like .NETs), though the + * internal representation is now of un-encoded parts, this will change the + * behavior slightly. + * + */ + +goog.provide('goog.Uri'); +goog.provide('goog.Uri.QueryData'); + +goog.require('goog.array'); +goog.require('goog.asserts'); +goog.require('goog.string'); +goog.require('goog.structs'); +goog.require('goog.structs.Map'); +goog.require('goog.uri.utils'); +goog.require('goog.uri.utils.ComponentIndex'); +goog.require('goog.uri.utils.StandardQueryParam'); + + + +/** + * This class contains setters and getters for the parts of the URI. + * The <code>getXyz</code>/<code>setXyz</code> methods return the decoded part + * -- so<code>goog.Uri.parse('/foo%20bar').getPath()</code> will return the + * decoded path, <code>/foo bar</code>. + * + * Reserved characters (see RFC 3986 section 2.2) can be present in + * their percent-encoded form in scheme, domain, and path URI components and + * will not be auto-decoded. For example: + * <code>goog.Uri.parse('rel%61tive/path%2fto/resource').getPath()</code> will + * return <code>relative/path%2fto/resource</code>. + * + * The constructor accepts an optional unparsed, raw URI string. The parser + * is relaxed, so special characters that aren't escaped but don't cause + * ambiguities will not cause parse failures. + * + * All setters return <code>this</code> and so may be chained, a la + * <code>goog.Uri.parse('/foo').setFragment('part').toString()</code>. + * + * @param {*=} opt_uri Optional string URI to parse + * (use goog.Uri.create() to create a URI from parts), or if + * a goog.Uri is passed, a clone is created. + * @param {boolean=} opt_ignoreCase If true, #getParameterValue will ignore + * the case of the parameter name. + * + * @throws URIError If opt_uri is provided and URI is malformed (that is, + * if decodeURIComponent fails on any of the URI components). + * @constructor + * @struct + */ +goog.Uri = function(opt_uri, opt_ignoreCase) { + /** + * Scheme such as "http". + * @private {string} + */ + this.scheme_ = ''; + + /** + * User credentials in the form "username:password". + * @private {string} + */ + this.userInfo_ = ''; + + /** + * Domain part, e.g. "www.google.com". + * @private {string} + */ + this.domain_ = ''; + + /** + * Port, e.g. 8080. + * @private {?number} + */ + this.port_ = null; + + /** + * Path, e.g. "/tests/img.png". + * @private {string} + */ + this.path_ = ''; + + /** + * The fragment without the #. + * @private {string} + */ + this.fragment_ = ''; + + /** + * Whether or not this Uri should be treated as Read Only. + * @private {boolean} + */ + this.isReadOnly_ = false; + + /** + * Whether or not to ignore case when comparing query params. + * @private {boolean} + */ + this.ignoreCase_ = false; + + /** + * Object representing query data. + * @private {!goog.Uri.QueryData} + */ + this.queryData_; + + // Parse in the uri string + var m; + if (opt_uri instanceof goog.Uri) { + this.ignoreCase_ = + goog.isDef(opt_ignoreCase) ? opt_ignoreCase : opt_uri.getIgnoreCase(); + this.setScheme(opt_uri.getScheme()); + this.setUserInfo(opt_uri.getUserInfo()); + this.setDomain(opt_uri.getDomain()); + this.setPort(opt_uri.getPort()); + this.setPath(opt_uri.getPath()); + this.setQueryData(opt_uri.getQueryData().clone()); + this.setFragment(opt_uri.getFragment()); + } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) { + this.ignoreCase_ = !!opt_ignoreCase; + + // Set the parts -- decoding as we do so. + // COMPATIBILITY NOTE - In IE, unmatched fields may be empty strings, + // whereas in other browsers they will be undefined. + this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true); + this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true); + this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true); + this.setPort(m[goog.uri.utils.ComponentIndex.PORT]); + this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true); + this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true); + this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true); + + } else { + this.ignoreCase_ = !!opt_ignoreCase; + this.queryData_ = new goog.Uri.QueryData(null, null, this.ignoreCase_); + } +}; + + +/** + * If true, we preserve the type of query parameters set programmatically. + * + * This means that if you set a parameter to a boolean, and then call + * getParameterValue, you will get a boolean back. + * + * If false, we will coerce parameters to strings, just as they would + * appear in real URIs. + * + * TODO(nicksantos): Remove this once people have time to fix all tests. + * + * @type {boolean} + */ +goog.Uri.preserveParameterTypesCompatibilityFlag = false; + + +/** + * Parameter name added to stop caching. + * @type {string} + */ +goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM; + + +/** + * @return {string} The string form of the url. + * @override + */ +goog.Uri.prototype.toString = function() { + var out = []; + + var scheme = this.getScheme(); + if (scheme) { + out.push( + goog.Uri.encodeSpecialChars_( + scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), + ':'); + } + + var domain = this.getDomain(); + if (domain || scheme == 'file') { + out.push('//'); + + var userInfo = this.getUserInfo(); + if (userInfo) { + out.push( + goog.Uri.encodeSpecialChars_( + userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_, true), + '@'); + } + + out.push(goog.Uri.removeDoubleEncoding_(goog.string.urlEncode(domain))); + + var port = this.getPort(); + if (port != null) { + out.push(':', String(port)); + } + } + + var path = this.getPath(); + if (path) { + if (this.hasDomain() && path.charAt(0) != '/') { + out.push('/'); + } + out.push( + goog.Uri.encodeSpecialChars_( + path, path.charAt(0) == '/' ? goog.Uri.reDisallowedInAbsolutePath_ : + goog.Uri.reDisallowedInRelativePath_, + true)); + } + + var query = this.getEncodedQuery(); + if (query) { + out.push('?', query); + } + + var fragment = this.getFragment(); + if (fragment) { + out.push( + '#', goog.Uri.encodeSpecialChars_( + fragment, goog.Uri.reDisallowedInFragment_)); + } + return out.join(''); +}; + + +/** + * Resolves the given relative URI (a goog.Uri object), using the URI + * represented by this instance as the base URI. + * + * There are several kinds of relative URIs:<br> + * 1. foo - replaces the last part of the path, the whole query and fragment<br> + * 2. /foo - replaces the the path, the query and fragment<br> + * 3. //foo - replaces everything from the domain on. foo is a domain name<br> + * 4. ?foo - replace the query and fragment<br> + * 5. #foo - replace the fragment only + * + * Additionally, if relative URI has a non-empty path, all ".." and "." + * segments will be resolved, as described in RFC 3986. + * + * @param {!goog.Uri} relativeUri The relative URI to resolve. + * @return {!goog.Uri} The resolved URI. + */ +goog.Uri.prototype.resolve = function(relativeUri) { + + var absoluteUri = this.clone(); + + // we satisfy these conditions by looking for the first part of relativeUri + // that is not blank and applying defaults to the rest + + var overridden = relativeUri.hasScheme(); + + if (overridden) { + absoluteUri.setScheme(relativeUri.getScheme()); + } else { + overridden = relativeUri.hasUserInfo(); + } + + if (overridden) { + absoluteUri.setUserInfo(relativeUri.getUserInfo()); + } else { + overridden = relativeUri.hasDomain(); + } + + if (overridden) { + absoluteUri.setDomain(relativeUri.getDomain()); + } else { + overridden = relativeUri.hasPort(); + } + + var path = relativeUri.getPath(); + if (overridden) { + absoluteUri.setPort(relativeUri.getPort()); + } else { + overridden = relativeUri.hasPath(); + if (overridden) { + // resolve path properly + if (path.charAt(0) != '/') { + // path is relative + if (this.hasDomain() && !this.hasPath()) { + // RFC 3986, section 5.2.3, case 1 + path = '/' + path; + } else { + // RFC 3986, section 5.2.3, case 2 + var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/'); + if (lastSlashIndex != -1) { + path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path; + } + } + } + path = goog.Uri.removeDotSegments(path); + } + } + + if (overridden) { + absoluteUri.setPath(path); + } else { + overridden = relativeUri.hasQuery(); + } + + if (overridden) { + absoluteUri.setQueryData(relativeUri.getDecodedQuery()); + } else { + overridden = relativeUri.hasFragment(); + } + + if (overridden) { + absoluteUri.setFragment(relativeUri.getFragment()); + } + + return absoluteUri; +}; + + +/** + * Clones the URI instance. + * @return {!goog.Uri} New instance of the URI object. + */ +goog.Uri.prototype.clone = function() { + return new goog.Uri(this); +}; + + +/** + * @return {string} The encoded scheme/protocol for the URI. + */ +goog.Uri.prototype.getScheme = function() { + return this.scheme_; +}; + + +/** + * Sets the scheme/protocol. + * @throws URIError If opt_decode is true and newScheme is malformed (that is, + * if decodeURIComponent fails). + * @param {string} newScheme New scheme value. + * @param {boolean=} opt_decode Optional param for whether to decode new value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setScheme = function(newScheme, opt_decode) { + this.enforceReadOnly(); + this.scheme_ = + opt_decode ? goog.Uri.decodeOrEmpty_(newScheme, true) : newScheme; + + // remove an : at the end of the scheme so somebody can pass in + // window.location.protocol + if (this.scheme_) { + this.scheme_ = this.scheme_.replace(/:$/, ''); + } + return this; +}; + + +/** + * @return {boolean} Whether the scheme has been set. + */ +goog.Uri.prototype.hasScheme = function() { + return !!this.scheme_; +}; + + +/** + * @return {string} The decoded user info. + */ +goog.Uri.prototype.getUserInfo = function() { + return this.userInfo_; +}; + + +/** + * Sets the userInfo. + * @throws URIError If opt_decode is true and newUserInfo is malformed (that is, + * if decodeURIComponent fails). + * @param {string} newUserInfo New userInfo value. + * @param {boolean=} opt_decode Optional param for whether to decode new value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) { + this.enforceReadOnly(); + this.userInfo_ = + opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) : newUserInfo; + return this; +}; + + +/** + * @return {boolean} Whether the user info has been set. + */ +goog.Uri.prototype.hasUserInfo = function() { + return !!this.userInfo_; +}; + + +/** + * @return {string} The decoded domain. + */ +goog.Uri.prototype.getDomain = function() { + return this.domain_; +}; + + +/** + * Sets the domain. + * @throws URIError If opt_decode is true and newDomain is malformed (that is, + * if decodeURIComponent fails). + * @param {string} newDomain New domain value. + * @param {boolean=} opt_decode Optional param for whether to decode new value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setDomain = function(newDomain, opt_decode) { + this.enforceReadOnly(); + this.domain_ = + opt_decode ? goog.Uri.decodeOrEmpty_(newDomain, true) : newDomain; + return this; +}; + + +/** + * @return {boolean} Whether the domain has been set. + */ +goog.Uri.prototype.hasDomain = function() { + return !!this.domain_; +}; + + +/** + * @return {?number} The port number. + */ +goog.Uri.prototype.getPort = function() { + return this.port_; +}; + + +/** + * Sets the port number. + * @param {*} newPort Port number. Will be explicitly casted to a number. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setPort = function(newPort) { + this.enforceReadOnly(); + + if (newPort) { + newPort = Number(newPort); + if (isNaN(newPort) || newPort < 0) { + throw Error('Bad port number ' + newPort); + } + this.port_ = newPort; + } else { + this.port_ = null; + } + + return this; +}; + + +/** + * @return {boolean} Whether the port has been set. + */ +goog.Uri.prototype.hasPort = function() { + return this.port_ != null; +}; + + +/** + * @return {string} The decoded path. + */ +goog.Uri.prototype.getPath = function() { + return this.path_; +}; + + +/** + * Sets the path. + * @throws URIError If opt_decode is true and newPath is malformed (that is, + * if decodeURIComponent fails). + * @param {string} newPath New path value. + * @param {boolean=} opt_decode Optional param for whether to decode new value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setPath = function(newPath, opt_decode) { + this.enforceReadOnly(); + this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath, true) : newPath; + return this; +}; + + +/** + * @return {boolean} Whether the path has been set. + */ +goog.Uri.prototype.hasPath = function() { + return !!this.path_; +}; + + +/** + * @return {boolean} Whether the query string has been set. + */ +goog.Uri.prototype.hasQuery = function() { + return this.queryData_.toString() !== ''; +}; + + +/** + * Sets the query data. + * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object. + * @param {boolean=} opt_decode Optional param for whether to decode new value. + * Applies only if queryData is a string. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setQueryData = function(queryData, opt_decode) { + this.enforceReadOnly(); + + if (queryData instanceof goog.Uri.QueryData) { + this.queryData_ = queryData; + this.queryData_.setIgnoreCase(this.ignoreCase_); + } else { + if (!opt_decode) { + // QueryData accepts encoded query string, so encode it if + // opt_decode flag is not true. + queryData = goog.Uri.encodeSpecialChars_( + queryData, goog.Uri.reDisallowedInQuery_); + } + this.queryData_ = new goog.Uri.QueryData(queryData, null, this.ignoreCase_); + } + + return this; +}; + + +/** + * Sets the URI query. + * @param {string} newQuery New query value. + * @param {boolean=} opt_decode Optional param for whether to decode new value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setQuery = function(newQuery, opt_decode) { + return this.setQueryData(newQuery, opt_decode); +}; + + +/** + * @return {string} The encoded URI query, not including the ?. + */ +goog.Uri.prototype.getEncodedQuery = function() { + return this.queryData_.toString(); +}; + + +/** + * @return {string} The decoded URI query, not including the ?. + */ +goog.Uri.prototype.getDecodedQuery = function() { + return this.queryData_.toDecodedString(); +}; + + +/** + * Returns the query data. + * @return {!goog.Uri.QueryData} QueryData object. + */ +goog.Uri.prototype.getQueryData = function() { + return this.queryData_; +}; + + +/** + * @return {string} The encoded URI query, not including the ?. + * + * Warning: This method, unlike other getter methods, returns encoded + * value, instead of decoded one. + */ +goog.Uri.prototype.getQuery = function() { + return this.getEncodedQuery(); +}; + + +/** + * Sets the value of the named query parameters, clearing previous values for + * that key. + * + * @param {string} key The parameter to set. + * @param {*} value The new value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setParameterValue = function(key, value) { + this.enforceReadOnly(); + this.queryData_.set(key, value); + return this; +}; + + +/** + * Sets the values of the named query parameters, clearing previous values for + * that key. Not new values will currently be moved to the end of the query + * string. + * + * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new']) + * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p> + * + * @param {string} key The parameter to set. + * @param {*} values The new values. If values is a single + * string then it will be treated as the sole value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setParameterValues = function(key, values) { + this.enforceReadOnly(); + + if (!goog.isArray(values)) { + values = [String(values)]; + } + + this.queryData_.setValues(key, values); + + return this; +}; + + +/** + * Returns the value<b>s</b> for a given cgi parameter as a list of decoded + * query parameter values. + * @param {string} name The parameter to get values for. + * @return {!Array<?>} The values for a given cgi parameter as a list of + * decoded query parameter values. + */ +goog.Uri.prototype.getParameterValues = function(name) { + return this.queryData_.getValues(name); +}; + + +/** + * Returns the first value for a given cgi parameter or undefined if the given + * parameter name does not appear in the query string. + * @param {string} paramName Unescaped parameter name. + * @return {string|undefined} The first value for a given cgi parameter or + * undefined if the given parameter name does not appear in the query + * string. + */ +goog.Uri.prototype.getParameterValue = function(paramName) { + // NOTE(nicksantos): This type-cast is a lie when + // preserveParameterTypesCompatibilityFlag is set to true. + // But this should only be set to true in tests. + return /** @type {string|undefined} */ (this.queryData_.get(paramName)); +}; + + +/** + * @return {string} The URI fragment, not including the #. + */ +goog.Uri.prototype.getFragment = function() { + return this.fragment_; +}; + + +/** + * Sets the URI fragment. + * @throws URIError If opt_decode is true and newFragment is malformed (that is, + * if decodeURIComponent fails). + * @param {string} newFragment New fragment value. + * @param {boolean=} opt_decode Optional param for whether to decode new value. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.setFragment = function(newFragment, opt_decode) { + this.enforceReadOnly(); + this.fragment_ = + opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) : newFragment; + return this; +}; + + +/** + * @return {boolean} Whether the URI has a fragment set. + */ +goog.Uri.prototype.hasFragment = function() { + return !!this.fragment_; +}; + + +/** + * Returns true if this has the same domain as that of uri2. + * @param {!goog.Uri} uri2 The URI object to compare to. + * @return {boolean} true if same domain; false otherwise. + */ +goog.Uri.prototype.hasSameDomainAs = function(uri2) { + return ((!this.hasDomain() && !uri2.hasDomain()) || + this.getDomain() == uri2.getDomain()) && + ((!this.hasPort() && !uri2.hasPort()) || + this.getPort() == uri2.getPort()); +}; + + +/** + * Adds a random parameter to the Uri. + * @return {!goog.Uri} Reference to this Uri object. + */ +goog.Uri.prototype.makeUnique = function() { + this.enforceReadOnly(); + this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString()); + + return this; +}; + + +/** + * Removes the named query parameter. + * + * @param {string} key The parameter to remove. + * @return {!goog.Uri} Reference to this URI object. + */ +goog.Uri.prototype.removeParameter = function(key) { + this.enforceReadOnly(); + this.queryData_.remove(key); + return this; +}; + + +/** + * Sets whether Uri is read only. If this goog.Uri is read-only, + * enforceReadOnly_ will be called at the start of any function that may modify + * this Uri. + * @param {boolean} isReadOnly whether this goog.Uri should be read only. + * @return {!goog.Uri} Reference to this Uri object. + */ +goog.Uri.prototype.setReadOnly = function(isReadOnly) { + this.isReadOnly_ = isReadOnly; + return this; +}; + + +/** + * @return {boolean} Whether the URI is read only. + */ +goog.Uri.prototype.isReadOnly = function() { + return this.isReadOnly_; +}; + + +/** + * Checks if this Uri has been marked as read only, and if so, throws an error. + * This should be called whenever any modifying function is called. + */ +goog.Uri.prototype.enforceReadOnly = function() { + if (this.isReadOnly_) { + throw Error('Tried to modify a read-only Uri'); + } +}; + + +/** + * Sets whether to ignore case. + * NOTE: If there are already key/value pairs in the QueryData, and + * ignoreCase_ is set to false, the keys will all be lower-cased. + * @param {boolean} ignoreCase whether this goog.Uri should ignore case. + * @return {!goog.Uri} Reference to this Uri object. + */ +goog.Uri.prototype.setIgnoreCase = function(ignoreCase) { + this.ignoreCase_ = ignoreCase; + if (this.queryData_) { + this.queryData_.setIgnoreCase(ignoreCase); + } + return this; +}; + + +/** + * @return {boolean} Whether to ignore case. + */ +goog.Uri.prototype.getIgnoreCase = function() { + return this.ignoreCase_; +}; + + +//============================================================================== +// Static members +//============================================================================== + + +/** + * Creates a uri from the string form. Basically an alias of new goog.Uri(). + * If a Uri object is passed to parse then it will return a clone of the object. + * + * @throws URIError If parsing the URI is malformed. The passed URI components + * should all be parseable by decodeURIComponent. + * @param {*} uri Raw URI string or instance of Uri + * object. + * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter + * names in #getParameterValue. + * @return {!goog.Uri} The new URI object. + */ +goog.Uri.parse = function(uri, opt_ignoreCase) { + return uri instanceof goog.Uri ? uri.clone() : + new goog.Uri(uri, opt_ignoreCase); +}; + + +/** + * Creates a new goog.Uri object from unencoded parts. + * + * @param {?string=} opt_scheme Scheme/protocol or full URI to parse. + * @param {?string=} opt_userInfo username:password. + * @param {?string=} opt_domain www.google.com. + * @param {?number=} opt_port 9830. + * @param {?string=} opt_path /some/path/to/a/file.html. + * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2. + * @param {?string=} opt_fragment The fragment without the #. + * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in + * #getParameterValue. + * + * @return {!goog.Uri} The new URI object. + */ +goog.Uri.create = function( + opt_scheme, opt_userInfo, opt_domain, opt_port, opt_path, opt_query, + opt_fragment, opt_ignoreCase) { + + var uri = new goog.Uri(null, opt_ignoreCase); + + // Only set the parts if they are defined and not empty strings. + opt_scheme && uri.setScheme(opt_scheme); + opt_userInfo && uri.setUserInfo(opt_userInfo); + opt_domain && uri.setDomain(opt_domain); + opt_port && uri.setPort(opt_port); + opt_path && uri.setPath(opt_path); + opt_query && uri.setQueryData(opt_query); + opt_fragment && uri.setFragment(opt_fragment); + + return uri; +}; + + +/** + * Resolves a relative Uri against a base Uri, accepting both strings and + * Uri objects. + * + * @param {*} base Base Uri. + * @param {*} rel Relative Uri. + * @return {!goog.Uri} Resolved uri. + */ +goog.Uri.resolve = function(base, rel) { + if (!(base instanceof goog.Uri)) { + base = goog.Uri.parse(base); + } + + if (!(rel instanceof goog.Uri)) { + rel = goog.Uri.parse(rel); + } + + return base.resolve(rel); +}; + + +/** + * Removes dot segments in given path component, as described in + * RFC 3986, section 5.2.4. + * + * @param {string} path A non-empty path component. + * @return {string} Path component with removed dot segments. + */ +goog.Uri.removeDotSegments = function(path) { + if (path == '..' || path == '.') { + return ''; + + } else if ( + !goog.string.contains(path, './') && !goog.string.contains(path, '/.')) { + // This optimization detects uris which do not contain dot-segments, + // and as a consequence do not require any processing. + return path; + + } else { + var leadingSlash = goog.string.startsWith(path, '/'); + var segments = path.split('/'); + var out = []; + + for (var pos = 0; pos < segments.length;) { + var segment = segments[pos++]; + + if (segment == '.') { + if (leadingSlash && pos == segments.length) { + out.push(''); + } + } else if (segment == '..') { + if (out.length > 1 || out.length == 1 && out[0] != '') { + out.pop(); + } + if (leadingSlash && pos == segments.length) { + out.push(''); + } + } else { + out.push(segment); + leadingSlash = true; + } + } + + return out.join('/'); + } +}; + + +/** + * Decodes a value or returns the empty string if it isn't defined or empty. + * @throws URIError If decodeURIComponent fails to decode val. + * @param {string|undefined} val Value to decode. + * @param {boolean=} opt_preserveReserved If true, restricted characters will + * not be decoded. + * @return {string} Decoded value. + * @private + */ +goog.Uri.decodeOrEmpty_ = function(val, opt_preserveReserved) { + // Don't use UrlDecode() here because val is not a query parameter. + if (!val) { + return ''; + } + + // decodeURI has the same output for '%2f' and '%252f'. We double encode %25 + // so that we can distinguish between the 2 inputs. This is later undone by + // removeDoubleEncoding_. + return opt_preserveReserved ? decodeURI(val.replace(/%25/g, '%2525')) : + decodeURIComponent(val); +}; + + +/** + * If unescapedPart is non null, then escapes any characters in it that aren't + * valid characters in a url and also escapes any special characters that + * appear in extra. + * + * @param {*} unescapedPart The string to encode. + * @param {RegExp} extra A character set of characters in [\01-\177]. + * @param {boolean=} opt_removeDoubleEncoding If true, remove double percent + * encoding. + * @return {?string} null iff unescapedPart == null. + * @private + */ +goog.Uri.encodeSpecialChars_ = function( + unescapedPart, extra, opt_removeDoubleEncoding) { + if (goog.isString(unescapedPart)) { + var encoded = encodeURI(unescapedPart).replace(extra, goog.Uri.encodeChar_); + if (opt_removeDoubleEncoding) { + // encodeURI double-escapes %XX sequences used to represent restricted + // characters in some URI components, remove the double escaping here. + encoded = goog.Uri.removeDoubleEncoding_(encoded); + } + return encoded; + } + return null; +}; + + +/** + * Converts a character in [\01-\177] to its unicode character equivalent. + * @param {string} ch One character string. + * @return {string} Encoded string. + * @private + */ +goog.Uri.encodeChar_ = function(ch) { + var n = ch.charCodeAt(0); + return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16); +}; + + +/** + * Removes double percent-encoding from a string. + * @param {string} doubleEncodedString String + * @return {string} String with double encoding removed. + * @private + */ +goog.Uri.removeDoubleEncoding_ = function(doubleEncodedString) { + return doubleEncodedString.replace(/%25([0-9a-fA-F]{2})/g, '%$1'); +}; + + +/** + * Regular expression for characters that are disallowed in the scheme or + * userInfo part of the URI. + * @type {RegExp} + * @private + */ +goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g; + + +/** + * Regular expression for characters that are disallowed in a relative path. + * Colon is included due to RFC 3986 3.3. + * @type {RegExp} + * @private + */ +goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g; + + +/** + * Regular expression for characters that are disallowed in an absolute path. + * @type {RegExp} + * @private + */ +goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g; + + +/** + * Regular expression for characters that are disallowed in the query. + * @type {RegExp} + * @private + */ +goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g; + + +/** + * Regular expression for characters that are disallowed in the fragment. + * @type {RegExp} + * @private + */ +goog.Uri.reDisallowedInFragment_ = /#/g; + + +/** + * Checks whether two URIs have the same domain. + * @param {string} uri1String First URI string. + * @param {string} uri2String Second URI string. + * @return {boolean} true if the two URIs have the same domain; false otherwise. + */ +goog.Uri.haveSameDomain = function(uri1String, uri2String) { + // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme. + // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain. + var pieces1 = goog.uri.utils.split(uri1String); + var pieces2 = goog.uri.utils.split(uri2String); + return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] == + pieces2[goog.uri.utils.ComponentIndex.DOMAIN] && + pieces1[goog.uri.utils.ComponentIndex.PORT] == + pieces2[goog.uri.utils.ComponentIndex.PORT]; +}; + + + +/** + * Class used to represent URI query parameters. It is essentially a hash of + * name-value pairs, though a name can be present more than once. + * + * Has the same interface as the collections in goog.structs. + * + * @param {?string=} opt_query Optional encoded query string to parse into + * the object. + * @param {goog.Uri=} opt_uri Optional uri object that should have its + * cache invalidated when this object updates. Deprecated -- this + * is no longer required. + * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter + * name in #get. + * @constructor + * @struct + * @final + */ +goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) { + /** + * The map containing name/value or name/array-of-values pairs. + * May be null if it requires parsing from the query string. + * + * We need to use a Map because we cannot guarantee that the key names will + * not be problematic for IE. + * + * @private {goog.structs.Map<string, !Array<*>>} + */ + this.keyMap_ = null; + + /** + * The number of params, or null if it requires computing. + * @private {?number} + */ + this.count_ = null; + + /** + * Encoded query string, or null if it requires computing from the key map. + * @private {?string} + */ + this.encodedQuery_ = opt_query || null; + + /** + * If true, ignore the case of the parameter name in #get. + * @private {boolean} + */ + this.ignoreCase_ = !!opt_ignoreCase; +}; + + +/** + * If the underlying key map is not yet initialized, it parses the + * query string and fills the map with parsed data. + * @private + */ +goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() { + if (!this.keyMap_) { + this.keyMap_ = new goog.structs.Map(); + this.count_ = 0; + if (this.encodedQuery_) { + var self = this; + goog.uri.utils.parseQueryData(this.encodedQuery_, function(name, value) { + self.add(goog.string.urlDecode(name), value); + }); + } + } +}; + + +/** + * Creates a new query data instance from a map of names and values. + * + * @param {!goog.structs.Map<string, ?>|!Object} map Map of string parameter + * names to parameter value. If parameter value is an array, it is + * treated as if the key maps to each individual value in the + * array. + * @param {goog.Uri=} opt_uri URI object that should have its cache + * invalidated when this object updates. + * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter + * name in #get. + * @return {!goog.Uri.QueryData} The populated query data instance. + */ +goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) { + var keys = goog.structs.getKeys(map); + if (typeof keys == 'undefined') { + throw Error('Keys are undefined'); + } + + var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase); + var values = goog.structs.getValues(map); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var value = values[i]; + if (!goog.isArray(value)) { + queryData.add(key, value); + } else { + queryData.setValues(key, value); + } + } + return queryData; +}; + + +/** + * Creates a new query data instance from parallel arrays of parameter names + * and values. Allows for duplicate parameter names. Throws an error if the + * lengths of the arrays differ. + * + * @param {!Array<string>} keys Parameter names. + * @param {!Array<?>} values Parameter values. + * @param {goog.Uri=} opt_uri URI object that should have its cache + * invalidated when this object updates. + * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter + * name in #get. + * @return {!goog.Uri.QueryData} The populated query data instance. + */ +goog.Uri.QueryData.createFromKeysValues = function( + keys, values, opt_uri, opt_ignoreCase) { + if (keys.length != values.length) { + throw Error('Mismatched lengths for keys/values'); + } + var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase); + for (var i = 0; i < keys.length; i++) { + queryData.add(keys[i], values[i]); + } + return queryData; +}; + + +/** + * @return {?number} The number of parameters. + */ +goog.Uri.QueryData.prototype.getCount = function() { + this.ensureKeyMapInitialized_(); + return this.count_; +}; + + +/** + * Adds a key value pair. + * @param {string} key Name. + * @param {*} value Value. + * @return {!goog.Uri.QueryData} Instance of this object. + */ +goog.Uri.QueryData.prototype.add = function(key, value) { + this.ensureKeyMapInitialized_(); + this.invalidateCache_(); + + key = this.getKeyName_(key); + var values = this.keyMap_.get(key); + if (!values) { + this.keyMap_.set(key, (values = [])); + } + values.push(value); + this.count_ = goog.asserts.assertNumber(this.count_) + 1; + return this; +}; + + +/** + * Removes all the params with the given key. + * @param {string} key Name. + * @return {boolean} Whether any parameter was removed. + */ +goog.Uri.QueryData.prototype.remove = function(key) { + this.ensureKeyMapInitialized_(); + + key = this.getKeyName_(key); + if (this.keyMap_.containsKey(key)) { + this.invalidateCache_(); + + // Decrement parameter count. + this.count_ = + goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length; + return this.keyMap_.remove(key); + } + return false; +}; + + +/** + * Clears the parameters. + */ +goog.Uri.QueryData.prototype.clear = function() { + this.invalidateCache_(); + this.keyMap_ = null; + this.count_ = 0; +}; + + +/** + * @return {boolean} Whether we have any parameters. + */ +goog.Uri.QueryData.prototype.isEmpty = function() { + this.ensureKeyMapInitialized_(); + return this.count_ == 0; +}; + + +/** + * Whether there is a parameter with the given name + * @param {string} key The parameter name to check for. + * @return {boolean} Whether there is a parameter with the given name. + */ +goog.Uri.QueryData.prototype.containsKey = function(key) { + this.ensureKeyMapInitialized_(); + key = this.getKeyName_(key); + return this.keyMap_.containsKey(key); +}; + + +/** + * Whether there is a parameter with the given value. + * @param {*} value The value to check for. + * @return {boolean} Whether there is a parameter with the given value. + */ +goog.Uri.QueryData.prototype.containsValue = function(value) { + // NOTE(arv): This solution goes through all the params even if it was the + // first param. We can get around this by not reusing code or by switching to + // iterators. + var vals = this.getValues(); + return goog.array.contains(vals, value); +}; + + +/** + * Returns all the keys of the parameters. If a key is used multiple times + * it will be included multiple times in the returned array + * @return {!Array<string>} All the keys of the parameters. + */ +goog.Uri.QueryData.prototype.getKeys = function() { + this.ensureKeyMapInitialized_(); + // We need to get the values to know how many keys to add. + var vals = this.keyMap_.getValues(); + var keys = this.keyMap_.getKeys(); + var rv = []; + for (var i = 0; i < keys.length; i++) { + var val = vals[i]; + for (var j = 0; j < val.length; j++) { + rv.push(keys[i]); + } + } + return rv; +}; + + +/** + * Returns all the values of the parameters with the given name. If the query + * data has no such key this will return an empty array. If no key is given + * all values wil be returned. + * @param {string=} opt_key The name of the parameter to get the values for. + * @return {!Array<?>} All the values of the parameters with the given name. + */ +goog.Uri.QueryData.prototype.getValues = function(opt_key) { + this.ensureKeyMapInitialized_(); + var rv = []; + if (goog.isString(opt_key)) { + if (this.containsKey(opt_key)) { + rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key))); + } + } else { + // Return all values. + var values = this.keyMap_.getValues(); + for (var i = 0; i < values.length; i++) { + rv = goog.array.concat(rv, values[i]); + } + } + return rv; +}; + + +/** + * Sets a key value pair and removes all other keys with the same value. + * + * @param {string} key Name. + * @param {*} value Value. + * @return {!goog.Uri.QueryData} Instance of this object. + */ +goog.Uri.QueryData.prototype.set = function(key, value) { + this.ensureKeyMapInitialized_(); + this.invalidateCache_(); + + // TODO(chrishenry): This could be better written as + // this.remove(key), this.add(key, value), but that would reorder + // the key (since the key is first removed and then added at the + // end) and we would have to fix unit tests that depend on key + // ordering. + key = this.getKeyName_(key); + if (this.containsKey(key)) { + this.count_ = + goog.asserts.assertNumber(this.count_) - this.keyMap_.get(key).length; + } + this.keyMap_.set(key, [value]); + this.count_ = goog.asserts.assertNumber(this.count_) + 1; + return this; +}; + + +/** + * Returns the first value associated with the key. If the query data has no + * such key this will return undefined or the optional default. + * @param {string} key The name of the parameter to get the value for. + * @param {*=} opt_default The default value to return if the query data + * has no such key. + * @return {*} The first string value associated with the key, or opt_default + * if there's no value. + */ +goog.Uri.QueryData.prototype.get = function(key, opt_default) { + var values = key ? this.getValues(key) : []; + if (goog.Uri.preserveParameterTypesCompatibilityFlag) { + return values.length > 0 ? values[0] : opt_default; + } else { + return values.length > 0 ? String(values[0]) : opt_default; + } +}; + + +/** + * Sets the values for a key. If the key already exists, this will + * override all of the existing values that correspond to the key. + * @param {string} key The key to set values for. + * @param {!Array<?>} values The values to set. + */ +goog.Uri.QueryData.prototype.setValues = function(key, values) { + this.remove(key); + + if (values.length > 0) { + this.invalidateCache_(); + this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values)); + this.count_ = goog.asserts.assertNumber(this.count_) + values.length; + } +}; + + +/** + * @return {string} Encoded query string. + * @override + */ +goog.Uri.QueryData.prototype.toString = function() { + if (this.encodedQuery_) { + return this.encodedQuery_; + } + + if (!this.keyMap_) { + return ''; + } + + var sb = []; + + // In the past, we use this.getKeys() and this.getVals(), but that + // generates a lot of allocations as compared to simply iterating + // over the keys. + var keys = this.keyMap_.getKeys(); + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var encodedKey = goog.string.urlEncode(key); + var val = this.getValues(key); + for (var j = 0; j < val.length; j++) { + var param = encodedKey; + // Ensure that null and undefined are encoded into the url as + // literal strings. + if (val[j] !== '') { + param += '=' + goog.string.urlEncode(val[j]); + } + sb.push(param); + } + } + + return this.encodedQuery_ = sb.join('&'); +}; + + +/** + * @throws URIError If URI is malformed (that is, if decodeURIComponent fails on + * any of the URI components). + * @return {string} Decoded query string. + */ +goog.Uri.QueryData.prototype.toDecodedString = function() { + return goog.Uri.decodeOrEmpty_(this.toString()); +}; + + +/** + * Invalidate the cache. + * @private + */ +goog.Uri.QueryData.prototype.invalidateCache_ = function() { + this.encodedQuery_ = null; +}; + + +/** + * Removes all keys that are not in the provided list. (Modifies this object.) + * @param {Array<string>} keys The desired keys. + * @return {!goog.Uri.QueryData} a reference to this object. + */ +goog.Uri.QueryData.prototype.filterKeys = function(keys) { + this.ensureKeyMapInitialized_(); + this.keyMap_.forEach(function(value, key) { + if (!goog.array.contains(keys, key)) { + this.remove(key); + } + }, this); + return this; +}; + + +/** + * Clone the query data instance. + * @return {!goog.Uri.QueryData} New instance of the QueryData object. + */ +goog.Uri.QueryData.prototype.clone = function() { + var rv = new goog.Uri.QueryData(); + rv.encodedQuery_ = this.encodedQuery_; + if (this.keyMap_) { + rv.keyMap_ = this.keyMap_.clone(); + rv.count_ = this.count_; + } + return rv; +}; + + +/** + * Helper function to get the key name from a JavaScript object. Converts + * the object to a string, and to lower case if necessary. + * @private + * @param {*} arg The object to get a key name from. + * @return {string} valid key name which can be looked up in #keyMap_. + */ +goog.Uri.QueryData.prototype.getKeyName_ = function(arg) { + var keyName = String(arg); + if (this.ignoreCase_) { + keyName = keyName.toLowerCase(); + } + return keyName; +}; + + +/** + * Ignore case in parameter names. + * NOTE: If there are already key/value pairs in the QueryData, and + * ignoreCase_ is set to false, the keys will all be lower-cased. + * @param {boolean} ignoreCase whether this goog.Uri should ignore case. + */ +goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) { + var resetKeys = ignoreCase && !this.ignoreCase_; + if (resetKeys) { + this.ensureKeyMapInitialized_(); + this.invalidateCache_(); + this.keyMap_.forEach(function(value, key) { + var lowerCase = key.toLowerCase(); + if (key != lowerCase) { + this.remove(key); + this.setValues(lowerCase, value); + } + }, this); + } + this.ignoreCase_ = ignoreCase; +}; + + +/** + * Extends a query data object with another query data or map like object. This + * operates 'in-place', it does not create a new QueryData object. + * + * @param {...(goog.Uri.QueryData|goog.structs.Map<?, ?>|Object)} var_args + * The object from which key value pairs will be copied. + */ +goog.Uri.QueryData.prototype.extend = function(var_args) { + for (var i = 0; i < arguments.length; i++) { + var data = arguments[i]; + goog.structs.forEach( + data, function(value, key) { this.add(key, value); }, this); + } +}; + +goog.provide('ol.style.Text'); + + +goog.require('ol.style.Fill'); + + +/** + * @classdesc + * Set text style for vector features. + * + * @constructor + * @param {olx.style.TextOptions=} opt_options Options. + * @api + */ +ol.style.Text = function(opt_options) { + + var options = opt_options || {}; + + /** + * @private + * @type {string|undefined} + */ + this.font_ = options.font; + + /** + * @private + * @type {number|undefined} + */ + this.rotation_ = options.rotation; + + /** + * @private + * @type {number|undefined} + */ + this.scale_ = options.scale; + + /** + * @private + * @type {string|undefined} + */ + this.text_ = options.text; + + /** + * @private + * @type {string|undefined} + */ + this.textAlign_ = options.textAlign; + + /** + * @private + * @type {string|undefined} + */ + this.textBaseline_ = options.textBaseline; + + /** + * @private + * @type {ol.style.Fill} + */ + this.fill_ = options.fill !== undefined ? options.fill : + new ol.style.Fill({color: ol.style.Text.DEFAULT_FILL_COLOR_}); + + /** + * @private + * @type {ol.style.Stroke} + */ + this.stroke_ = options.stroke !== undefined ? options.stroke : null; + + /** + * @private + * @type {number} + */ + this.offsetX_ = options.offsetX !== undefined ? options.offsetX : 0; + + /** + * @private + * @type {number} + */ + this.offsetY_ = options.offsetY !== undefined ? options.offsetY : 0; +}; + + +/** + * The default fill color to use if no fill was set at construction time; a + * blackish `#333`. + * + * @const {string} + * @private + */ +ol.style.Text.DEFAULT_FILL_COLOR_ = '#333'; + + +/** + * Get the font name. + * @return {string|undefined} Font. + * @api + */ +ol.style.Text.prototype.getFont = function() { + return this.font_; +}; + + +/** + * Get the x-offset for the text. + * @return {number} Horizontal text offset. + * @api + */ +ol.style.Text.prototype.getOffsetX = function() { + return this.offsetX_; +}; + + +/** + * Get the y-offset for the text. + * @return {number} Vertical text offset. + * @api + */ +ol.style.Text.prototype.getOffsetY = function() { + return this.offsetY_; +}; + + +/** + * Get the fill style for the text. + * @return {ol.style.Fill} Fill style. + * @api + */ +ol.style.Text.prototype.getFill = function() { + return this.fill_; +}; + + +/** + * Get the text rotation. + * @return {number|undefined} Rotation. + * @api + */ +ol.style.Text.prototype.getRotation = function() { + return this.rotation_; +}; + + +/** + * Get the text scale. + * @return {number|undefined} Scale. + * @api + */ +ol.style.Text.prototype.getScale = function() { + return this.scale_; +}; + + +/** + * Get the stroke style for the text. + * @return {ol.style.Stroke} Stroke style. + * @api + */ +ol.style.Text.prototype.getStroke = function() { + return this.stroke_; +}; + + +/** + * Get the text to be rendered. + * @return {string|undefined} Text. + * @api + */ +ol.style.Text.prototype.getText = function() { + return this.text_; +}; + + +/** + * Get the text alignment. + * @return {string|undefined} Text align. + * @api + */ +ol.style.Text.prototype.getTextAlign = function() { + return this.textAlign_; +}; + + +/** + * Get the text baseline. + * @return {string|undefined} Text baseline. + * @api + */ +ol.style.Text.prototype.getTextBaseline = function() { + return this.textBaseline_; +}; + + +/** + * Set the font. + * + * @param {string|undefined} font Font. + * @api + */ +ol.style.Text.prototype.setFont = function(font) { + this.font_ = font; +}; + + +/** + * Set the x offset. + * + * @param {number} offsetX Horizontal text offset. + * @api + */ +ol.style.Text.prototype.setOffsetX = function(offsetX) { + this.offsetX_ = offsetX; +}; + + +/** + * Set the y offset. + * + * @param {number} offsetY Vertical text offset. + * @api + */ +ol.style.Text.prototype.setOffsetY = function(offsetY) { + this.offsetY_ = offsetY; +}; + + +/** + * Set the fill. + * + * @param {ol.style.Fill} fill Fill style. + * @api + */ +ol.style.Text.prototype.setFill = function(fill) { + this.fill_ = fill; +}; + + +/** + * Set the rotation. + * + * @param {number|undefined} rotation Rotation. + * @api + */ +ol.style.Text.prototype.setRotation = function(rotation) { + this.rotation_ = rotation; +}; + + +/** + * Set the scale. + * + * @param {number|undefined} scale Scale. + * @api + */ +ol.style.Text.prototype.setScale = function(scale) { + this.scale_ = scale; +}; + + +/** + * Set the stroke. + * + * @param {ol.style.Stroke} stroke Stroke style. + * @api + */ +ol.style.Text.prototype.setStroke = function(stroke) { + this.stroke_ = stroke; +}; + + +/** + * Set the text. + * + * @param {string|undefined} text Text. + * @api + */ +ol.style.Text.prototype.setText = function(text) { + this.text_ = text; +}; + + +/** + * Set the text alignment. + * + * @param {string|undefined} textAlign Text align. + * @api + */ +ol.style.Text.prototype.setTextAlign = function(textAlign) { + this.textAlign_ = textAlign; +}; + + +/** + * Set the text baseline. + * + * @param {string|undefined} textBaseline Text baseline. + * @api + */ +ol.style.Text.prototype.setTextBaseline = function(textBaseline) { + this.textBaseline_ = textBaseline; +}; + +// FIXME http://earth.google.com/kml/1.0 namespace? +// FIXME why does node.getAttribute return an unknown type? +// FIXME serialize arbitrary feature properties +// FIXME don't parse style if extractStyles is false + +goog.provide('ol.format.KML'); + +goog.require('goog.Uri'); +goog.require('goog.asserts'); +goog.require('goog.object'); +goog.require('ol'); +goog.require('ol.Feature'); +goog.require('ol.array'); +goog.require('ol.color'); +goog.require('ol.format.Feature'); +goog.require('ol.format.XMLFeature'); +goog.require('ol.format.XSD'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.math'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Icon'); +goog.require('ol.style.IconAnchorUnits'); +goog.require('ol.style.IconOrigin'); +goog.require('ol.style.Image'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); +goog.require('ol.style.Text'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Feature format for reading and writing data in the KML format. + * + * @constructor + * @extends {ol.format.XMLFeature} + * @param {olx.format.KMLOptions=} opt_options Options. + * @api stable + */ +ol.format.KML = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.XMLFeature.call(this); + + /** + * @inheritDoc + */ + this.defaultDataProjection = ol.proj.get('EPSG:4326'); + + /** + * @private + * @type {Array.<ol.style.Style>} + */ + this.defaultStyle_ = options.defaultStyle ? + options.defaultStyle : ol.format.KML.DEFAULT_STYLE_ARRAY_; + + /** + * @private + * @type {boolean} + */ + this.extractStyles_ = options.extractStyles !== undefined ? + options.extractStyles : true; + + /** + * @private + * @type {boolean} + */ + this.writeStyles_ = options.writeStyles !== undefined ? + options.writeStyles : true; + + /** + * @private + * @type {Object.<string, (Array.<ol.style.Style>|string)>} + */ + this.sharedStyles_ = {}; + + /** + * @private + * @type {boolean} + */ + this.showPointNames_ = options.showPointNames !== undefined ? + options.showPointNames : true; + +}; +ol.inherits(ol.format.KML, ol.format.XMLFeature); + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.KML.EXTENSIONS_ = ['.kml']; + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.KML.GX_NAMESPACE_URIS_ = [ + 'http://www.google.com/kml/ext/2.2' +]; + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.KML.NAMESPACE_URIS_ = [ + null, + 'http://earth.google.com/kml/2.0', + 'http://earth.google.com/kml/2.1', + 'http://earth.google.com/kml/2.2', + 'http://www.opengis.net/kml/2.2' +]; + + +/** + * @const + * @type {string} + * @private + */ +ol.format.KML.SCHEMA_LOCATION_ = 'http://www.opengis.net/kml/2.2 ' + + 'https://developers.google.com/kml/schema/kml22gx.xsd'; + + +/** + * @const + * @type {ol.Color} + * @private + */ +ol.format.KML.DEFAULT_COLOR_ = [255, 255, 255, 1]; + + +/** + * @const + * @type {ol.style.Fill} + * @private + */ +ol.format.KML.DEFAULT_FILL_STYLE_ = new ol.style.Fill({ + color: ol.format.KML.DEFAULT_COLOR_ +}); + + +/** + * @const + * @type {ol.Size} + * @private + */ +ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_ = [20, 2]; // FIXME maybe [8, 32] ? + + +/** + * @const + * @type {ol.style.IconAnchorUnits} + * @private + */ +ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_ = + ol.style.IconAnchorUnits.PIXELS; + + +/** + * @const + * @type {ol.style.IconAnchorUnits} + * @private + */ +ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_ = + ol.style.IconAnchorUnits.PIXELS; + + +/** + * @const + * @type {ol.Size} + * @private + */ +ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_ = [64, 64]; + + +/** + * @const + * @type {string} + * @private + */ +ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_ = + 'https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png'; + + +/** + * @const + * @type {number} + * @private + */ +ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_ = 0.5; + + +/** + * @const + * @type {ol.style.Image} + * @private + */ +ol.format.KML.DEFAULT_IMAGE_STYLE_ = new ol.style.Icon({ + anchor: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_, + anchorOrigin: ol.style.IconOrigin.BOTTOM_LEFT, + anchorXUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_, + anchorYUnits: ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_, + crossOrigin: 'anonymous', + rotation: 0, + scale: ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_, + size: ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_, + src: ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_ +}); + + +/** + * @const + * @type {ol.style.Stroke} + * @private + */ +ol.format.KML.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({ + color: ol.format.KML.DEFAULT_COLOR_, + width: 1 +}); + + +/** + * @const + * @type {ol.style.Stroke} + * @private + */ +ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_ = new ol.style.Stroke({ + color: [51, 51, 51, 1], + width: 2 +}); + + +/** + * @const + * @type {ol.style.Text} + * @private + */ +ol.format.KML.DEFAULT_TEXT_STYLE_ = new ol.style.Text({ + font: 'bold 16px Helvetica', + fill: ol.format.KML.DEFAULT_FILL_STYLE_, + stroke: ol.format.KML.DEFAULT_TEXT_STROKE_STYLE_, + scale: 0.8 +}); + + +/** + * @const + * @type {ol.style.Style} + * @private + */ +ol.format.KML.DEFAULT_STYLE_ = new ol.style.Style({ + fill: ol.format.KML.DEFAULT_FILL_STYLE_, + image: ol.format.KML.DEFAULT_IMAGE_STYLE_, + text: ol.format.KML.DEFAULT_TEXT_STYLE_, + stroke: ol.format.KML.DEFAULT_STROKE_STYLE_, + zIndex: 0 +}); + + +/** + * @const + * @type {Array.<ol.style.Style>} + * @private + */ +ol.format.KML.DEFAULT_STYLE_ARRAY_ = [ol.format.KML.DEFAULT_STYLE_]; + + +/** + * @const + * @type {Object.<string, ol.style.IconAnchorUnits>} + * @private + */ +ol.format.KML.ICON_ANCHOR_UNITS_MAP_ = { + 'fraction': ol.style.IconAnchorUnits.FRACTION, + 'pixels': ol.style.IconAnchorUnits.PIXELS +}; + + +/** + * @param {ol.style.Style|undefined} foundStyle Style. + * @param {string} name Name. + * @return {ol.style.Style} style Style. + * @private + */ +ol.format.KML.createNameStyleFunction_ = function(foundStyle, name) { + var textStyle = null; + var textOffset = [0, 0]; + var textAlign = 'start'; + if (foundStyle.getImage()) { + var imageSize = foundStyle.getImage().getImageSize(); + if (imageSize && imageSize.length == 2) { + // Offset the label to be centered to the right of the icon, if there is + // one. + textOffset[0] = foundStyle.getImage().getScale() * imageSize[0] / 2; + textOffset[1] = -foundStyle.getImage().getScale() * imageSize[1] / 2; + textAlign = 'left'; + } + } + if (!ol.object.isEmpty(foundStyle.getText())) { + textStyle = /** @type {ol.style.Text} */ + (goog.object.clone(foundStyle.getText())); + textStyle.setText(name); + textStyle.setTextAlign(textAlign); + textStyle.setOffsetX(textOffset[0]); + textStyle.setOffsetY(textOffset[1]); + } else { + textStyle = new ol.style.Text({ + text: name, + offsetX: textOffset[0], + offsetY: textOffset[1], + textAlign: textAlign + }); + } + var nameStyle = new ol.style.Style({ + text: textStyle + }); + return nameStyle; +}; + + +/** + * @param {Array.<ol.style.Style>|undefined} style Style. + * @param {string} styleUrl Style URL. + * @param {Array.<ol.style.Style>} defaultStyle Default style. + * @param {Object.<string, (Array.<ol.style.Style>|string)>} sharedStyles Shared + * styles. + * @param {boolean|undefined} showPointNames true to show names for point + * placemarks. + * @return {ol.FeatureStyleFunction} Feature style function. + * @private + */ +ol.format.KML.createFeatureStyleFunction_ = function(style, styleUrl, + defaultStyle, sharedStyles, showPointNames) { + + return ( + /** + * @param {number} resolution Resolution. + * @return {Array.<ol.style.Style>} Style. + * @this {ol.Feature} + */ + function(resolution) { + var drawName = showPointNames; + /** @type {ol.style.Style|undefined} */ + var nameStyle; + var name = ''; + if (drawName) { + if (this.getGeometry()) { + drawName = (this.getGeometry().getType() === + ol.geom.GeometryType.POINT); + } + } + + if (drawName) { + name = /** @type {string} */ (this.get('name')); + drawName = drawName && name; + } + + if (style) { + if (drawName) { + nameStyle = ol.format.KML.createNameStyleFunction_(style[0], + name); + return style.concat(nameStyle); + } + return style; + } + if (styleUrl) { + var foundStyle = ol.format.KML.findStyle_(styleUrl, defaultStyle, + sharedStyles); + if (drawName) { + nameStyle = ol.format.KML.createNameStyleFunction_(foundStyle[0], + name); + return foundStyle.concat(nameStyle); + } + return foundStyle; + } + if (drawName) { + nameStyle = ol.format.KML.createNameStyleFunction_(defaultStyle[0], + name); + return defaultStyle.concat(nameStyle); + } + return defaultStyle; + }); +}; + + +/** + * @param {Array.<ol.style.Style>|string|undefined} styleValue Style value. + * @param {Array.<ol.style.Style>} defaultStyle Default style. + * @param {Object.<string, (Array.<ol.style.Style>|string)>} sharedStyles + * Shared styles. + * @return {Array.<ol.style.Style>} Style. + * @private + */ +ol.format.KML.findStyle_ = function(styleValue, defaultStyle, sharedStyles) { + if (Array.isArray(styleValue)) { + return styleValue; + } else if (typeof styleValue === 'string') { + // KML files in the wild occasionally forget the leading `#` on styleUrls + // defined in the same document. Add a leading `#` if it enables to find + // a style. + if (!(styleValue in sharedStyles) && ('#' + styleValue in sharedStyles)) { + styleValue = '#' + styleValue; + } + return ol.format.KML.findStyle_( + sharedStyles[styleValue], defaultStyle, sharedStyles); + } else { + return defaultStyle; + } +}; + + +/** + * @param {Node} node Node. + * @private + * @return {ol.Color|undefined} Color. + */ +ol.format.KML.readColor_ = function(node) { + var s = ol.xml.getAllTextContent(node, false); + // The KML specification states that colors should not include a leading `#` + // but we tolerate them. + var m = /^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(s); + if (m) { + var hexColor = m[1]; + return [ + parseInt(hexColor.substr(6, 2), 16), + parseInt(hexColor.substr(4, 2), 16), + parseInt(hexColor.substr(2, 2), 16), + parseInt(hexColor.substr(0, 2), 16) / 255 + ]; + + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @private + * @return {Array.<number>|undefined} Flat coordinates. + */ +ol.format.KML.readFlatCoordinates_ = function(node) { + var s = ol.xml.getAllTextContent(node, false); + var flatCoordinates = []; + // The KML specification states that coordinate tuples should not include + // spaces, but we tolerate them. + var re = + /^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i; + var m; + while ((m = re.exec(s))) { + var x = parseFloat(m[1]); + var y = parseFloat(m[2]); + var z = m[3] ? parseFloat(m[3]) : 0; + flatCoordinates.push(x, y, z); + s = s.substr(m[0].length); + } + if (s !== '') { + return undefined; + } + return flatCoordinates; +}; + + +/** + * @param {Node} node Node. + * @private + * @return {string|undefined} Style URL. + */ +ol.format.KML.readStyleUrl_ = function(node) { + var s = ol.xml.getAllTextContent(node, false).trim(); + if (node.baseURI) { + return goog.Uri.resolve(node.baseURI, s).toString(); + } else { + return s; + } + +}; + + +/** + * @param {Node} node Node. + * @private + * @return {string} URI. + */ +ol.format.KML.readURI_ = function(node) { + var s = ol.xml.getAllTextContent(node, false); + if (node.baseURI) { + return goog.Uri.resolve(node.baseURI, s.trim()).toString(); + } else { + return s.trim(); + } +}; + + +/** + * @param {Node} node Node. + * @private + * @return {ol.KMLVec2_} Vec2. + */ +ol.format.KML.readVec2_ = function(node) { + var xunits = node.getAttribute('xunits'); + var yunits = node.getAttribute('yunits'); + return { + x: parseFloat(node.getAttribute('x')), + xunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[xunits], + y: parseFloat(node.getAttribute('y')), + yunits: ol.format.KML.ICON_ANCHOR_UNITS_MAP_[yunits] + }; +}; + + +/** + * @param {Node} node Node. + * @private + * @return {number|undefined} Scale. + */ +ol.format.KML.readScale_ = function(node) { + var number = ol.format.XSD.readDecimal(node); + if (number !== undefined) { + return Math.sqrt(number); + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<ol.style.Style>|string|undefined} StyleMap. + */ +ol.format.KML.readStyleMapValue_ = function(node, objectStack) { + return ol.xml.pushParseAndPop(undefined, + ol.format.KML.STYLE_MAP_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.IconStyleParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be an ELEMENT'); + goog.asserts.assert(node.localName == 'IconStyle', + 'localName should be IconStyle'); + // FIXME refreshMode + // FIXME refreshInterval + // FIXME viewRefreshTime + // FIXME viewBoundScale + // FIXME viewFormat + // FIXME httpQuery + var object = ol.xml.pushParseAndPop( + {}, ol.format.KML.ICON_STYLE_PARSERS_, node, objectStack); + if (!object) { + return; + } + var styleObject = /** @type {Object} */ (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isObject(styleObject), + 'styleObject should be an Object'); + var IconObject = 'Icon' in object ? object['Icon'] : {}; + var src; + var href = /** @type {string|undefined} */ + (IconObject['href']); + if (href) { + src = href; + } else { + src = ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_; + } + var anchor, anchorXUnits, anchorYUnits; + var hotSpot = /** @type {ol.KMLVec2_|undefined} */ + (object['hotSpot']); + if (hotSpot) { + anchor = [hotSpot.x, hotSpot.y]; + anchorXUnits = hotSpot.xunits; + anchorYUnits = hotSpot.yunits; + } else if (src === ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) { + anchor = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_; + anchorXUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_X_UNITS_; + anchorYUnits = ol.format.KML.DEFAULT_IMAGE_STYLE_ANCHOR_Y_UNITS_; + } else if (/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(src)) { + anchor = [0.5, 0]; + anchorXUnits = ol.style.IconAnchorUnits.FRACTION; + anchorYUnits = ol.style.IconAnchorUnits.FRACTION; + } + + var offset; + var x = /** @type {number|undefined} */ + (IconObject['x']); + var y = /** @type {number|undefined} */ + (IconObject['y']); + if (x !== undefined && y !== undefined) { + offset = [x, y]; + } + + var size; + var w = /** @type {number|undefined} */ + (IconObject['w']); + var h = /** @type {number|undefined} */ + (IconObject['h']); + if (w !== undefined && h !== undefined) { + size = [w, h]; + } + + var rotation; + var heading = /** @type {number} */ + (object['heading']); + if (heading !== undefined) { + rotation = ol.math.toRadians(heading); + } + + var scale = /** @type {number|undefined} */ + (object['scale']); + if (src == ol.format.KML.DEFAULT_IMAGE_STYLE_SRC_) { + size = ol.format.KML.DEFAULT_IMAGE_STYLE_SIZE_; + if (scale === undefined) { + scale = ol.format.KML.DEFAULT_IMAGE_SCALE_MULTIPLIER_; + } + } + + var imageStyle = new ol.style.Icon({ + anchor: anchor, + anchorOrigin: ol.style.IconOrigin.BOTTOM_LEFT, + anchorXUnits: anchorXUnits, + anchorYUnits: anchorYUnits, + crossOrigin: 'anonymous', // FIXME should this be configurable? + offset: offset, + offsetOrigin: ol.style.IconOrigin.BOTTOM_LEFT, + rotation: rotation, + scale: scale, + size: size, + src: src + }); + styleObject['imageStyle'] = imageStyle; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.LabelStyleParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LabelStyle', + 'localName should be LabelStyle'); + // FIXME colorMode + var object = ol.xml.pushParseAndPop( + {}, ol.format.KML.LABEL_STYLE_PARSERS_, node, objectStack); + if (!object) { + return; + } + var styleObject = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(styleObject), + 'styleObject should be an Object'); + var textStyle = new ol.style.Text({ + fill: new ol.style.Fill({ + color: /** @type {ol.Color} */ + ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_) + }), + scale: /** @type {number|undefined} */ + (object['scale']) + }); + styleObject['textStyle'] = textStyle; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.LineStyleParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LineStyle', + 'localName should be LineStyle'); + // FIXME colorMode + // FIXME gx:outerColor + // FIXME gx:outerWidth + // FIXME gx:physicalWidth + // FIXME gx:labelVisibility + var object = ol.xml.pushParseAndPop( + {}, ol.format.KML.LINE_STYLE_PARSERS_, node, objectStack); + if (!object) { + return; + } + var styleObject = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(styleObject), + 'styleObject should be an Object'); + var strokeStyle = new ol.style.Stroke({ + color: /** @type {ol.Color} */ + ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_), + width: /** @type {number} */ ('width' in object ? object['width'] : 1) + }); + styleObject['strokeStyle'] = strokeStyle; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.PolyStyleParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'PolyStyle', + 'localName should be PolyStyle'); + // FIXME colorMode + var object = ol.xml.pushParseAndPop( + {}, ol.format.KML.POLY_STYLE_PARSERS_, node, objectStack); + if (!object) { + return; + } + var styleObject = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(styleObject), + 'styleObject should be an Object'); + var fillStyle = new ol.style.Fill({ + color: /** @type {ol.Color} */ + ('color' in object ? object['color'] : ol.format.KML.DEFAULT_COLOR_) + }); + styleObject['fillStyle'] = fillStyle; + var fill = /** @type {boolean|undefined} */ (object['fill']); + if (fill !== undefined) { + styleObject['fill'] = fill; + } + var outline = + /** @type {boolean|undefined} */ (object['outline']); + if (outline !== undefined) { + styleObject['outline'] = outline; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>} LinearRing flat coordinates. + */ +ol.format.KML.readFlatLinearRing_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LinearRing', + 'localName should be LinearRing'); + return ol.xml.pushParseAndPop(null, + ol.format.KML.FLAT_LINEAR_RING_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.gxCoordParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(ol.array.includes( + ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI), + 'namespaceURI of the node should be known to the KML parser'); + goog.asserts.assert(node.localName == 'coord', 'localName should be coord'); + var gxTrackObject = /** @type {ol.KMLGxTrackObject_} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isObject(gxTrackObject), + 'gxTrackObject should be an Object'); + var flatCoordinates = gxTrackObject.flatCoordinates; + var s = ol.xml.getAllTextContent(node, false); + var re = + /^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i; + var m = re.exec(s); + if (m) { + var x = parseFloat(m[1]); + var y = parseFloat(m[2]); + var z = parseFloat(m[3]); + flatCoordinates.push(x, y, z, 0); + } else { + flatCoordinates.push(0, 0, 0, 0); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.MultiLineString|undefined} MultiLineString. + */ +ol.format.KML.readGxMultiTrack_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(ol.array.includes( + ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI), + 'namespaceURI of the node should be known to the KML parser'); + goog.asserts.assert(node.localName == 'MultiTrack', + 'localName should be MultiTrack'); + var lineStrings = ol.xml.pushParseAndPop([], + ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_, node, objectStack); + if (!lineStrings) { + return undefined; + } + var multiLineString = new ol.geom.MultiLineString(null); + multiLineString.setLineStrings(lineStrings); + return multiLineString; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.LineString|undefined} LineString. + */ +ol.format.KML.readGxTrack_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(ol.array.includes( + ol.format.KML.GX_NAMESPACE_URIS_, node.namespaceURI), + 'namespaceURI of the node should be known to the KML parser'); + goog.asserts.assert(node.localName == 'Track', 'localName should be Track'); + var gxTrackObject = ol.xml.pushParseAndPop( + /** @type {ol.KMLGxTrackObject_} */ ({ + flatCoordinates: [], + whens: [] + }), ol.format.KML.GX_TRACK_PARSERS_, node, objectStack); + if (!gxTrackObject) { + return undefined; + } + var flatCoordinates = gxTrackObject.flatCoordinates; + var whens = gxTrackObject.whens; + goog.asserts.assert(flatCoordinates.length / 4 == whens.length, + 'the length of the flatCoordinates array divided by 4 should be the ' + + 'length of the whens array'); + var i, ii; + for (i = 0, ii = Math.min(flatCoordinates.length, whens.length); i < ii; + ++i) { + flatCoordinates[4 * i + 3] = whens[i]; + } + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZM, flatCoordinates); + return lineString; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object} Icon object. + */ +ol.format.KML.readIcon_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Icon', 'localName should be Icon'); + var iconObject = ol.xml.pushParseAndPop( + {}, ol.format.KML.ICON_PARSERS_, node, objectStack); + if (iconObject) { + return iconObject; + } else { + return null; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<number>} Flat coordinates. + */ +ol.format.KML.readFlatCoordinatesFromNode_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + return ol.xml.pushParseAndPop(null, + ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.LineString|undefined} LineString. + */ +ol.format.KML.readLineString_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LineString', + 'localName should be LineString'); + var properties = ol.xml.pushParseAndPop({}, + ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, + objectStack); + var flatCoordinates = + ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + var lineString = new ol.geom.LineString(null); + lineString.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + lineString.setProperties(properties); + return lineString; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Polygon|undefined} Polygon. + */ +ol.format.KML.readLinearRing_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'LinearRing', + 'localName should be LinearRing'); + var properties = ol.xml.pushParseAndPop({}, + ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, + objectStack); + var flatCoordinates = + ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + var polygon = new ol.geom.Polygon(null); + polygon.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates, + [flatCoordinates.length]); + polygon.setProperties(properties); + return polygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.KML.readMultiGeometry_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'MultiGeometry', + 'localName should be MultiGeometry'); + var geometries = ol.xml.pushParseAndPop([], + ol.format.KML.MULTI_GEOMETRY_PARSERS_, node, objectStack); + if (!geometries) { + return null; + } + if (geometries.length === 0) { + return new ol.geom.GeometryCollection(geometries); + } + var homogeneous = true; + var type = geometries[0].getType(); + var geometry, i, ii; + for (i = 1, ii = geometries.length; i < ii; ++i) { + geometry = geometries[i]; + if (geometry.getType() != type) { + homogeneous = false; + break; + } + } + if (homogeneous) { + var layout; + var flatCoordinates; + if (type == ol.geom.GeometryType.POINT) { + var point = geometries[0]; + goog.asserts.assertInstanceof(point, ol.geom.Point, + 'point should be an ol.geom.Point'); + layout = point.getLayout(); + flatCoordinates = point.getFlatCoordinates(); + for (i = 1, ii = geometries.length; i < ii; ++i) { + geometry = geometries[i]; + goog.asserts.assertInstanceof(geometry, ol.geom.Point, + 'geometry should be an ol.geom.Point'); + goog.asserts.assert(geometry.getLayout() == layout, + 'geometry layout should be consistent'); + ol.array.extend(flatCoordinates, geometry.getFlatCoordinates()); + } + var multiPoint = new ol.geom.MultiPoint(null); + multiPoint.setFlatCoordinates(layout, flatCoordinates); + ol.format.KML.setCommonGeometryProperties_(multiPoint, geometries); + return multiPoint; + } else if (type == ol.geom.GeometryType.LINE_STRING) { + var multiLineString = new ol.geom.MultiLineString(null); + multiLineString.setLineStrings(geometries); + ol.format.KML.setCommonGeometryProperties_(multiLineString, geometries); + return multiLineString; + } else if (type == ol.geom.GeometryType.POLYGON) { + var multiPolygon = new ol.geom.MultiPolygon(null); + multiPolygon.setPolygons(geometries); + ol.format.KML.setCommonGeometryProperties_(multiPolygon, geometries); + return multiPolygon; + } else if (type == ol.geom.GeometryType.GEOMETRY_COLLECTION) { + return new ol.geom.GeometryCollection(geometries); + } else { + goog.asserts.fail('Unexpected type: ' + type); + return null; + } + } else { + return new ol.geom.GeometryCollection(geometries); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Point|undefined} Point. + */ +ol.format.KML.readPoint_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Point', 'localName should be Point'); + var properties = ol.xml.pushParseAndPop({}, + ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, + objectStack); + var flatCoordinates = + ol.format.KML.readFlatCoordinatesFromNode_(node, objectStack); + if (flatCoordinates) { + var point = new ol.geom.Point(null); + goog.asserts.assert(flatCoordinates.length == 3, + 'flatCoordinates should have a length of 3'); + point.setFlatCoordinates(ol.geom.GeometryLayout.XYZ, flatCoordinates); + point.setProperties(properties); + return point; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.geom.Polygon|undefined} Polygon. + */ +ol.format.KML.readPolygon_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Polygon', + 'localName should be Polygon'); + var properties = ol.xml.pushParseAndPop(/** @type {Object<string,*>} */ ({}), + ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_, node, + objectStack); + var flatLinearRings = ol.xml.pushParseAndPop([null], + ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_, node, objectStack); + if (flatLinearRings && flatLinearRings[0]) { + var polygon = new ol.geom.Polygon(null); + var flatCoordinates = flatLinearRings[0]; + var ends = [flatCoordinates.length]; + var i, ii; + for (i = 1, ii = flatLinearRings.length; i < ii; ++i) { + ol.array.extend(flatCoordinates, flatLinearRings[i]); + ends.push(flatCoordinates.length); + } + polygon.setFlatCoordinates( + ol.geom.GeometryLayout.XYZ, flatCoordinates, ends); + polygon.setProperties(properties); + return polygon; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<ol.style.Style>} Style. + */ +ol.format.KML.readStyle_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Style', 'localName should be Style'); + var styleObject = ol.xml.pushParseAndPop( + {}, ol.format.KML.STYLE_PARSERS_, node, objectStack); + if (!styleObject) { + return null; + } + var fillStyle = /** @type {ol.style.Fill} */ + ('fillStyle' in styleObject ? + styleObject['fillStyle'] : ol.format.KML.DEFAULT_FILL_STYLE_); + var fill = /** @type {boolean|undefined} */ (styleObject['fill']); + if (fill !== undefined && !fill) { + fillStyle = null; + } + var imageStyle = /** @type {ol.style.Image} */ + ('imageStyle' in styleObject ? + styleObject['imageStyle'] : ol.format.KML.DEFAULT_IMAGE_STYLE_); + var textStyle = /** @type {ol.style.Text} */ + ('textStyle' in styleObject ? + styleObject['textStyle'] : ol.format.KML.DEFAULT_TEXT_STYLE_); + var strokeStyle = /** @type {ol.style.Stroke} */ + ('strokeStyle' in styleObject ? + styleObject['strokeStyle'] : ol.format.KML.DEFAULT_STROKE_STYLE_); + var outline = /** @type {boolean|undefined} */ + (styleObject['outline']); + if (outline !== undefined && !outline) { + strokeStyle = null; + } + return [new ol.style.Style({ + fill: fillStyle, + image: imageStyle, + stroke: strokeStyle, + text: textStyle, + zIndex: undefined // FIXME + })]; +}; + + +/** + * Reads an array of geometries and creates arrays for common geometry + * properties. Then sets them to the multi geometry. + * @param {ol.geom.MultiPoint|ol.geom.MultiLineString|ol.geom.MultiPolygon} + * multiGeometry A multi-geometry. + * @param {Array.<ol.geom.Geometry>} geometries List of geometries. + * @private + */ +ol.format.KML.setCommonGeometryProperties_ = function(multiGeometry, + geometries) { + var ii = geometries.length; + var extrudes = new Array(geometries.length); + var altitudeModes = new Array(geometries.length); + var geometry, i, hasExtrude, hasAltitudeMode; + hasExtrude = hasAltitudeMode = false; + for (i = 0; i < ii; ++i) { + geometry = geometries[i]; + extrudes[i] = geometry.get('extrude'); + altitudeModes[i] = geometry.get('altitudeMode'); + hasExtrude = hasExtrude || extrudes[i] !== undefined; + hasAltitudeMode = hasAltitudeMode || altitudeModes[i]; + } + if (hasExtrude) { + multiGeometry.set('extrude', extrudes); + } + if (hasAltitudeMode) { + multiGeometry.set('altitudeMode', altitudeModes); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.DataParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Data', 'localName should be Data'); + var name = node.getAttribute('name'); + if (name !== null) { + var data = ol.xml.pushParseAndPop( + undefined, ol.format.KML.DATA_PARSERS_, node, objectStack); + if (data) { + var featureObject = + /** @type {Object} */ (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isObject(featureObject), + 'featureObject should be an Object'); + featureObject[name] = data; + } + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.ExtendedDataParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'ExtendedData', + 'localName should be ExtendedData'); + ol.xml.parseNode(ol.format.KML.EXTENDED_DATA_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.PairDataParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Pair', 'localName should be Pair'); + var pairObject = ol.xml.pushParseAndPop( + {}, ol.format.KML.PAIR_PARSERS_, node, objectStack); + if (!pairObject) { + return; + } + var key = /** @type {string|undefined} */ + (pairObject['key']); + if (key && key == 'normal') { + var styleUrl = /** @type {string|undefined} */ + (pairObject['styleUrl']); + if (styleUrl) { + objectStack[objectStack.length - 1] = styleUrl; + } + var Style = /** @type {ol.style.Style} */ + (pairObject['Style']); + if (Style) { + objectStack[objectStack.length - 1] = Style; + } + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.PlacemarkStyleMapParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'StyleMap', + 'localName should be StyleMap'); + var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack); + if (!styleMapValue) { + return; + } + var placemarkObject = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(placemarkObject), + 'placemarkObject should be an Object'); + if (Array.isArray(styleMapValue)) { + placemarkObject['Style'] = styleMapValue; + } else if (typeof styleMapValue === 'string') { + placemarkObject['styleUrl'] = styleMapValue; + } else { + goog.asserts.fail('styleMapValue has an unknown type'); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.SchemaDataParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'SchemaData', + 'localName should be SchemaData'); + ol.xml.parseNode(ol.format.KML.SCHEMA_DATA_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.SimpleDataParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'SimpleData', + 'localName should be SimpleData'); + var name = node.getAttribute('name'); + if (name !== null) { + var data = ol.format.XSD.readString(node); + var featureObject = + /** @type {Object} */ (objectStack[objectStack.length - 1]); + featureObject[name] = data; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.innerBoundaryIsParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'innerBoundaryIs', + 'localName should be innerBoundaryIs'); + /** @type {Array.<number>|undefined} */ + var flatLinearRing = ol.xml.pushParseAndPop(undefined, + ol.format.KML.INNER_BOUNDARY_IS_PARSERS_, node, objectStack); + if (flatLinearRing) { + var flatLinearRings = /** @type {Array.<Array.<number>>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(Array.isArray(flatLinearRings), + 'flatLinearRings should be an array'); + goog.asserts.assert(flatLinearRings.length > 0, + 'flatLinearRings array should not be empty'); + flatLinearRings.push(flatLinearRing); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.outerBoundaryIsParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'outerBoundaryIs', + 'localName should be outerBoundaryIs'); + /** @type {Array.<number>|undefined} */ + var flatLinearRing = ol.xml.pushParseAndPop(undefined, + ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_, node, objectStack); + if (flatLinearRing) { + var flatLinearRings = /** @type {Array.<Array.<number>>} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(Array.isArray(flatLinearRings), + 'flatLinearRings should be an array'); + goog.asserts.assert(flatLinearRings.length > 0, + 'flatLinearRings array should not be empty'); + flatLinearRings[0] = flatLinearRing; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.LinkParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Link', 'localName should be Link'); + ol.xml.parseNode(ol.format.KML.LINK_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.whenParser_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'when', 'localName should be when'); + var gxTrackObject = /** @type {ol.KMLGxTrackObject_} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isObject(gxTrackObject), + 'gxTrackObject should be an Object'); + var whens = gxTrackObject.whens; + var s = ol.xml.getAllTextContent(node, false); + var re = + /^\s*(\d{4})($|-(\d{2})($|-(\d{2})($|T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?)))))\s*$/; + var m = re.exec(s); + if (m) { + var year = parseInt(m[1], 10); + var month = m[3] ? parseInt(m[3], 10) - 1 : 0; + var day = m[5] ? parseInt(m[5], 10) : 1; + var hour = m[7] ? parseInt(m[7], 10) : 0; + var minute = m[8] ? parseInt(m[8], 10) : 0; + var second = m[9] ? parseInt(m[9], 10) : 0; + var when = Date.UTC(year, month, day, hour, minute, second); + if (m[10] && m[10] != 'Z') { + var sign = m[11] == '-' ? -1 : 1; + when += sign * 60 * parseInt(m[12], 10); + if (m[13]) { + when += sign * 60 * 60 * parseInt(m[13], 10); + } + } + whens.push(when); + } else { + whens.push(0); + } +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.DATA_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'value': ol.xml.makeReplacer(ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.EXTENDED_DATA_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Data': ol.format.KML.DataParser_, + 'SchemaData': ol.format.KML.SchemaDataParser_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.EXTRUDE_AND_ALTITUDE_MODE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'extrude': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), + 'altitudeMode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.FLAT_LINEAR_RING_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.FLAT_LINEAR_RINGS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'innerBoundaryIs': ol.format.KML.innerBoundaryIsParser_, + 'outerBoundaryIs': ol.format.KML.outerBoundaryIsParser_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.GX_TRACK_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'when': ol.format.KML.whenParser_ + }, ol.xml.makeStructureNS( + ol.format.KML.GX_NAMESPACE_URIS_, { + 'coord': ol.format.KML.gxCoordParser_ + })); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.GEOMETRY_FLAT_COORDINATES_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'coordinates': ol.xml.makeReplacer(ol.format.KML.readFlatCoordinates_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.ICON_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_) + }, ol.xml.makeStructureNS( + ol.format.KML.GX_NAMESPACE_URIS_, { + 'x': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'y': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'w': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'h': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal) + })); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.ICON_STYLE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Icon': ol.xml.makeObjectPropertySetter(ol.format.KML.readIcon_), + 'heading': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal), + 'hotSpot': ol.xml.makeObjectPropertySetter(ol.format.KML.readVec2_), + 'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.INNER_BOUNDARY_IS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.LABEL_STYLE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_), + 'scale': ol.xml.makeObjectPropertySetter(ol.format.KML.readScale_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.LINE_STYLE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_), + 'width': ol.xml.makeObjectPropertySetter(ol.format.XSD.readDecimal) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.MULTI_GEOMETRY_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'LineString': ol.xml.makeArrayPusher(ol.format.KML.readLineString_), + 'LinearRing': ol.xml.makeArrayPusher(ol.format.KML.readLinearRing_), + 'MultiGeometry': ol.xml.makeArrayPusher(ol.format.KML.readMultiGeometry_), + 'Point': ol.xml.makeArrayPusher(ol.format.KML.readPoint_), + 'Polygon': ol.xml.makeArrayPusher(ol.format.KML.readPolygon_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.GX_MULTITRACK_GEOMETRY_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.GX_NAMESPACE_URIS_, { + 'Track': ol.xml.makeArrayPusher(ol.format.KML.readGxTrack_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.NETWORK_LINK_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'ExtendedData': ol.format.KML.ExtendedDataParser_, + 'Link': ol.format.KML.LinkParser_, + 'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), + 'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.LINK_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'href': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.OUTER_BOUNDARY_IS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'LinearRing': ol.xml.makeReplacer(ol.format.KML.readFlatLinearRing_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.PAIR_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_), + 'key': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyleUrl_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.PLACEMARK_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'ExtendedData': ol.format.KML.ExtendedDataParser_, + 'MultiGeometry': ol.xml.makeObjectPropertySetter( + ol.format.KML.readMultiGeometry_, 'geometry'), + 'LineString': ol.xml.makeObjectPropertySetter( + ol.format.KML.readLineString_, 'geometry'), + 'LinearRing': ol.xml.makeObjectPropertySetter( + ol.format.KML.readLinearRing_, 'geometry'), + 'Point': ol.xml.makeObjectPropertySetter( + ol.format.KML.readPoint_, 'geometry'), + 'Polygon': ol.xml.makeObjectPropertySetter( + ol.format.KML.readPolygon_, 'geometry'), + 'Style': ol.xml.makeObjectPropertySetter(ol.format.KML.readStyle_), + 'StyleMap': ol.format.KML.PlacemarkStyleMapParser_, + 'address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'description': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'open': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), + 'phoneNumber': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'styleUrl': ol.xml.makeObjectPropertySetter(ol.format.KML.readURI_), + 'visibility': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean) + }, ol.xml.makeStructureNS( + ol.format.KML.GX_NAMESPACE_URIS_, { + 'MultiTrack': ol.xml.makeObjectPropertySetter( + ol.format.KML.readGxMultiTrack_, 'geometry'), + 'Track': ol.xml.makeObjectPropertySetter( + ol.format.KML.readGxTrack_, 'geometry') + } + )); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.POLY_STYLE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeObjectPropertySetter(ol.format.KML.readColor_), + 'fill': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean), + 'outline': ol.xml.makeObjectPropertySetter(ol.format.XSD.readBoolean) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.SCHEMA_DATA_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'SimpleData': ol.format.KML.SimpleDataParser_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.STYLE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'IconStyle': ol.format.KML.IconStyleParser_, + 'LabelStyle': ol.format.KML.LabelStyleParser_, + 'LineStyle': ol.format.KML.LineStyleParser_, + 'PolyStyle': ol.format.KML.PolyStyleParser_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.KML.STYLE_MAP_PARSERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Pair': ol.format.KML.PairDataParser_ + }); + + +/** + * @inheritDoc + */ +ol.format.KML.prototype.getExtensions = function() { + return ol.format.KML.EXTENSIONS_; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<ol.Feature>|undefined} Features. + */ +ol.format.KML.prototype.readDocumentOrFolder_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + var localName = node.localName; + goog.asserts.assert(localName == 'Document' || localName == 'Folder', + 'localName should be Document or Folder'); + // FIXME use scope somehow + var parsersNS = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Document': ol.xml.makeArrayExtender(this.readDocumentOrFolder_, this), + 'Folder': ol.xml.makeArrayExtender(this.readDocumentOrFolder_, this), + 'Placemark': ol.xml.makeArrayPusher(this.readPlacemark_, this), + 'Style': this.readSharedStyle_.bind(this), + 'StyleMap': this.readSharedStyleMap_.bind(this) + }); + /** @type {Array.<ol.Feature>} */ + var features = ol.xml.pushParseAndPop([], parsersNS, node, objectStack, this); + if (features) { + return features; + } else { + return undefined; + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {ol.Feature|undefined} Feature. + */ +ol.format.KML.prototype.readPlacemark_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Placemark', + 'localName should be Placemark'); + var object = ol.xml.pushParseAndPop({'geometry': null}, + ol.format.KML.PLACEMARK_PARSERS_, node, objectStack); + if (!object) { + return undefined; + } + var feature = new ol.Feature(); + var id = node.getAttribute('id'); + if (id !== null) { + feature.setId(id); + } + var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); + + var geometry = object['geometry']; + if (geometry) { + ol.format.Feature.transformWithOptions(geometry, false, options); + } + feature.setGeometry(geometry); + delete object['geometry']; + + if (this.extractStyles_) { + var style = object['Style']; + var styleUrl = object['styleUrl']; + var styleFunction = ol.format.KML.createFeatureStyleFunction_( + style, styleUrl, this.defaultStyle_, this.sharedStyles_, + this.showPointNames_); + feature.setStyle(styleFunction); + } + delete object['Style']; + // we do not remove the styleUrl property from the object, so it + // gets stored on feature when setProperties is called + + feature.setProperties(object); + + return feature; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.prototype.readSharedStyle_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Style', 'localName should be Style'); + var id = node.getAttribute('id'); + if (id !== null) { + var style = ol.format.KML.readStyle_(node, objectStack); + if (style) { + var styleUri; + if (node.baseURI) { + styleUri = goog.Uri.resolve(node.baseURI, '#' + id).toString(); + } else { + styleUri = '#' + id; + } + this.sharedStyles_[styleUri] = style; + } + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.prototype.readSharedStyleMap_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'StyleMap', + 'localName should be StyleMap'); + var id = node.getAttribute('id'); + if (id === null) { + return; + } + var styleMapValue = ol.format.KML.readStyleMapValue_(node, objectStack); + if (!styleMapValue) { + return; + } + var styleUri; + if (node.baseURI) { + styleUri = goog.Uri.resolve(node.baseURI, '#' + id).toString(); + } else { + styleUri = '#' + id; + } + this.sharedStyles_[styleUri] = styleMapValue; +}; + + +/** + * Read the first feature from a KML source. MultiGeometries are converted into + * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ + * MultiLineString/MultiPolygon if they are all of the same type. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @api stable + */ +ol.format.KML.prototype.readFeature; + + +/** + * @inheritDoc + */ +ol.format.KML.prototype.readFeatureFromNode = function(node, opt_options) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + if (!ol.array.includes(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) { + return null; + } + goog.asserts.assert(node.localName == 'Placemark', + 'localName should be Placemark'); + var feature = this.readPlacemark_( + node, [this.getReadOptions(node, opt_options)]); + if (feature) { + return feature; + } else { + return null; + } +}; + + +/** + * Read all features from a KML source. MultiGeometries are converted into + * GeometryCollections if they are a mix of geometry types, and into MultiPoint/ + * MultiLineString/MultiPolygon if they are all of the same type. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.KML.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.KML.prototype.readFeaturesFromNode = function(node, opt_options) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + if (!ol.array.includes(ol.format.KML.NAMESPACE_URIS_, node.namespaceURI)) { + return []; + } + var features; + var localName = node.localName; + if (localName == 'Document' || localName == 'Folder') { + features = this.readDocumentOrFolder_( + node, [this.getReadOptions(node, opt_options)]); + if (features) { + return features; + } else { + return []; + } + } else if (localName == 'Placemark') { + var feature = this.readPlacemark_( + node, [this.getReadOptions(node, opt_options)]); + if (feature) { + return [feature]; + } else { + return []; + } + } else if (localName == 'kml') { + features = []; + var n; + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + var fs = this.readFeaturesFromNode(n, opt_options); + if (fs) { + ol.array.extend(features, fs); + } + } + return features; + } else { + return []; + } +}; + + +/** + * Read the name of the KML. + * + * @param {Document|Node|string} source Souce. + * @return {string|undefined} Name. + * @api stable + */ +ol.format.KML.prototype.readName = function(source) { + if (ol.xml.isDocument(source)) { + return this.readNameFromDocument(/** @type {Document} */ (source)); + } else if (ol.xml.isNode(source)) { + return this.readNameFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readNameFromDocument(doc); + } else { + goog.asserts.fail('Unknown type for source'); + return undefined; + } +}; + + +/** + * @param {Document} doc Document. + * @return {string|undefined} Name. + */ +ol.format.KML.prototype.readNameFromDocument = function(doc) { + var n; + for (n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + var name = this.readNameFromNode(n); + if (name) { + return name; + } + } + } + return undefined; +}; + + +/** + * @param {Node} node Node. + * @return {string|undefined} Name. + */ +ol.format.KML.prototype.readNameFromNode = function(node) { + var n; + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && + n.localName == 'name') { + return ol.format.XSD.readString(n); + } + } + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + var localName = n.localName; + if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && + (localName == 'Document' || + localName == 'Folder' || + localName == 'Placemark' || + localName == 'kml')) { + var name = this.readNameFromNode(n); + if (name) { + return name; + } + } + } + return undefined; +}; + + +/** + * Read the network links of the KML. + * + * @param {Document|Node|string} source Source. + * @return {Array.<Object>} Network links. + * @api + */ +ol.format.KML.prototype.readNetworkLinks = function(source) { + var networkLinks = []; + if (ol.xml.isDocument(source)) { + ol.array.extend(networkLinks, this.readNetworkLinksFromDocument( + /** @type {Document} */ (source))); + } else if (ol.xml.isNode(source)) { + ol.array.extend(networkLinks, this.readNetworkLinksFromNode( + /** @type {Node} */ (source))); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + ol.array.extend(networkLinks, this.readNetworkLinksFromDocument(doc)); + } else { + goog.asserts.fail('unknown type for source'); + } + return networkLinks; +}; + + +/** + * @param {Document} doc Document. + * @return {Array.<Object>} Network links. + */ +ol.format.KML.prototype.readNetworkLinksFromDocument = function(doc) { + var n, networkLinks = []; + for (n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + ol.array.extend(networkLinks, this.readNetworkLinksFromNode(n)); + } + } + return networkLinks; +}; + + +/** + * @param {Node} node Node. + * @return {Array.<Object>} Network links. + */ +ol.format.KML.prototype.readNetworkLinksFromNode = function(node) { + var n, networkLinks = []; + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && + n.localName == 'NetworkLink') { + var obj = ol.xml.pushParseAndPop({}, ol.format.KML.NETWORK_LINK_PARSERS_, + n, []); + networkLinks.push(obj); + } + } + for (n = node.firstElementChild; n; n = n.nextElementSibling) { + var localName = n.localName; + if (ol.array.includes(ol.format.KML.NAMESPACE_URIS_, n.namespaceURI) && + (localName == 'Document' || + localName == 'Folder' || + localName == 'kml')) { + ol.array.extend(networkLinks, this.readNetworkLinksFromNode(n)); + } + } + return networkLinks; +}; + + +/** + * Read the projection from a KML source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + * @api stable + */ +ol.format.KML.prototype.readProjection; + + +/** + * @param {Node} node Node to append a TextNode with the color to. + * @param {ol.Color|string} color Color. + * @private + */ +ol.format.KML.writeColorTextNode_ = function(node, color) { + var rgba = ol.color.asArray(color); + var opacity = (rgba.length == 4) ? rgba[3] : 1; + var abgr = [opacity * 255, rgba[2], rgba[1], rgba[0]]; + var i; + for (i = 0; i < 4; ++i) { + var hex = parseInt(abgr[i], 10).toString(16); + abgr[i] = (hex.length == 1) ? '0' + hex : hex; + } + ol.format.XSD.writeStringTextNode(node, abgr.join('')); +}; + + +/** + * @param {Node} node Node to append a TextNode with the coordinates to. + * @param {Array.<number>} coordinates Coordinates. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeCoordinatesTextNode_ = function(node, coordinates, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + + var layout = context['layout']; + var stride = context['stride']; + + var dimension; + if (layout == ol.geom.GeometryLayout.XY || + layout == ol.geom.GeometryLayout.XYM) { + dimension = 2; + } else if (layout == ol.geom.GeometryLayout.XYZ || + layout == ol.geom.GeometryLayout.XYZM) { + dimension = 3; + } else { + goog.asserts.fail('Unknown geometry layout'); + } + + var d, i; + var ii = coordinates.length; + var text = ''; + if (ii > 0) { + text += coordinates[0]; + for (d = 1; d < dimension; ++d) { + text += ',' + coordinates[d]; + } + for (i = stride; i < ii; i += stride) { + text += ' ' + coordinates[i]; + for (d = 1; d < dimension; ++d) { + text += ',' + coordinates[i + d]; + } + } + } + ol.format.XSD.writeStringTextNode(node, text); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<ol.Feature>} features Features. + * @param {Array.<*>} objectStack Object stack. + * @this {ol.format.KML} + * @private + */ +ol.format.KML.writeDocument_ = function(node, features, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + ol.xml.pushSerializeAndPop(context, ol.format.KML.DOCUMENT_SERIALIZERS_, + ol.format.KML.DOCUMENT_NODE_FACTORY_, features, objectStack, undefined, + this); +}; + + +/** + * @param {Node} node Node. + * @param {Object} icon Icon object. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeIcon_ = function(node, icon, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.ICON_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(icon, orderedKeys); + ol.xml.pushSerializeAndPop(context, + ol.format.KML.ICON_SERIALIZERS_, ol.xml.OBJECT_PROPERTY_NODE_FACTORY, + values, objectStack, orderedKeys); + orderedKeys = + ol.format.KML.ICON_SEQUENCE_[ol.format.KML.GX_NAMESPACE_URIS_[0]]; + values = ol.xml.makeSequence(icon, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_SERIALIZERS_, + ol.format.KML.GX_NODE_FACTORY_, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Icon} style Icon style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeIconStyle_ = function(node, style, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + var properties = {}; + var src = style.getSrc(); + var size = style.getSize(); + var iconImageSize = style.getImageSize(); + var iconProperties = { + 'href': src + }; + + if (size) { + iconProperties['w'] = size[0]; + iconProperties['h'] = size[1]; + var anchor = style.getAnchor(); // top-left + var origin = style.getOrigin(); // top-left + + if (origin && iconImageSize && origin[0] !== 0 && origin[1] !== size[1]) { + iconProperties['x'] = origin[0]; + iconProperties['y'] = iconImageSize[1] - (origin[1] + size[1]); + } + + if (anchor && anchor[0] !== 0 && anchor[1] !== size[1]) { + var /** @type {ol.KMLVec2_} */ hotSpot = { + x: anchor[0], + xunits: ol.style.IconAnchorUnits.PIXELS, + y: size[1] - anchor[1], + yunits: ol.style.IconAnchorUnits.PIXELS + }; + properties['hotSpot'] = hotSpot; + } + } + + properties['Icon'] = iconProperties; + + var scale = style.getScale(); + if (scale !== 1) { + properties['scale'] = scale; + } + + var rotation = style.getRotation(); + if (rotation !== 0) { + properties['heading'] = rotation; // 0-360 + } + + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.ICON_STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.ICON_STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Text} style style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeLabelStyle_ = function(node, style, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + var properties = {}; + var fill = style.getFill(); + if (fill) { + properties['color'] = fill.getColor(); + } + var scale = style.getScale(); + if (scale && scale !== 1) { + properties['scale'] = scale; + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = + ol.format.KML.LABEL_STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.LABEL_STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Stroke} style style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeLineStyle_ = function(node, style, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + var properties = { + 'color': style.getColor(), + 'width': style.getWidth() + }; + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.LINE_STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.LINE_STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Geometry} geometry Geometry. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeMultiGeometry_ = function(node, geometry, objectStack) { + goog.asserts.assert( + (geometry instanceof ol.geom.GeometryCollection) || + (geometry instanceof ol.geom.MultiPoint) || + (geometry instanceof ol.geom.MultiLineString) || + (geometry instanceof ol.geom.MultiPolygon), + 'geometry should be one of: ol.geom.GeometryCollection, ' + + 'ol.geom.MultiPoint, ol.geom.MultiLineString or ol.geom.MultiPolygon'); + /** @type {ol.XmlNodeStackItem} */ + var context = {node: node}; + var type = geometry.getType(); + /** @type {Array.<ol.geom.Geometry>} */ + var geometries; + /** @type {function(*, Array.<*>, string=): (Node|undefined)} */ + var factory; + if (type == ol.geom.GeometryType.GEOMETRY_COLLECTION) { + geometries = geometry.getGeometries(); + factory = ol.format.KML.GEOMETRY_NODE_FACTORY_; + } else if (type == ol.geom.GeometryType.MULTI_POINT) { + geometries = + (/** @type {ol.geom.MultiPoint} */ (geometry)).getPoints(); + factory = ol.format.KML.POINT_NODE_FACTORY_; + } else if (type == ol.geom.GeometryType.MULTI_LINE_STRING) { + geometries = + (/** @type {ol.geom.MultiLineString} */ (geometry)).getLineStrings(); + factory = ol.format.KML.LINE_STRING_NODE_FACTORY_; + } else if (type == ol.geom.GeometryType.MULTI_POLYGON) { + geometries = + (/** @type {ol.geom.MultiPolygon} */ (geometry)).getPolygons(); + factory = ol.format.KML.POLYGON_NODE_FACTORY_; + } else { + goog.asserts.fail('Unknown geometry type: ' + type); + } + ol.xml.pushSerializeAndPop(context, + ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_, factory, + geometries, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.LinearRing} linearRing Linear ring. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeBoundaryIs_ = function(node, linearRing, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + ol.xml.pushSerializeAndPop(context, + ol.format.KML.BOUNDARY_IS_SERIALIZERS_, + ol.format.KML.LINEAR_RING_NODE_FACTORY_, [linearRing], objectStack); +}; + + +/** + * FIXME currently we do serialize arbitrary/custom feature properties + * (ExtendedData). + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Object stack. + * @this {ol.format.KML} + * @private + */ +ol.format.KML.writePlacemark_ = function(node, feature, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + + // set id + if (feature.getId()) { + node.setAttribute('id', feature.getId()); + } + + // serialize properties (properties unknown to KML are not serialized) + var properties = feature.getProperties(); + + var styleFunction = feature.getStyleFunction(); + if (styleFunction) { + // FIXME the styles returned by the style function are supposed to be + // resolution-independent here + var styles = styleFunction.call(feature, 0); + if (styles) { + var style = Array.isArray(styles) ? styles[0] : styles; + if (this.writeStyles_) { + properties['Style'] = style; + } + var textStyle = style.getText(); + if (textStyle) { + properties['name'] = textStyle.getText(); + } + } + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.PLACEMARK_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); + + // serialize geometry + var options = /** @type {olx.format.WriteOptions} */ (objectStack[0]); + var geometry = feature.getGeometry(); + if (geometry) { + geometry = + ol.format.Feature.transformWithOptions(geometry, true, options); + } + ol.xml.pushSerializeAndPop(context, ol.format.KML.PLACEMARK_SERIALIZERS_, + ol.format.KML.GEOMETRY_NODE_FACTORY_, [geometry], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.SimpleGeometry} geometry Geometry. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writePrimitiveGeometry_ = function(node, geometry, objectStack) { + goog.asserts.assert( + (geometry instanceof ol.geom.Point) || + (geometry instanceof ol.geom.LineString) || + (geometry instanceof ol.geom.LinearRing), + 'geometry should be one of ol.geom.Point, ol.geom.LineString ' + + 'or ol.geom.LinearRing'); + var flatCoordinates = geometry.getFlatCoordinates(); + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + context['layout'] = geometry.getLayout(); + context['stride'] = geometry.getStride(); + ol.xml.pushSerializeAndPop(context, + ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_, + ol.format.KML.COORDINATES_NODE_FACTORY_, + [flatCoordinates], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.geom.Polygon} polygon Polygon. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writePolygon_ = function(node, polygon, objectStack) { + goog.asserts.assertInstanceof(polygon, ol.geom.Polygon, + 'polygon should be an ol.geom.Polygon'); + var linearRings = polygon.getLinearRings(); + goog.asserts.assert(linearRings.length > 0, + 'linearRings should not be empty'); + var outerRing = linearRings.shift(); + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + // inner rings + ol.xml.pushSerializeAndPop(context, + ol.format.KML.POLYGON_SERIALIZERS_, + ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_, + linearRings, objectStack); + // outer ring + ol.xml.pushSerializeAndPop(context, + ol.format.KML.POLYGON_SERIALIZERS_, + ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_, + [outerRing], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Fill} style Style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writePolyStyle_ = function(node, style, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + ol.xml.pushSerializeAndPop(context, ol.format.KML.POLY_STYLE_SERIALIZERS_, + ol.format.KML.COLOR_NODE_FACTORY_, [style.getColor()], objectStack); +}; + + +/** + * @param {Node} node Node to append a TextNode with the scale to. + * @param {number|undefined} scale Scale. + * @private + */ +ol.format.KML.writeScaleTextNode_ = function(node, scale) { + // the Math is to remove any excess decimals created by float arithmetic + ol.format.XSD.writeDecimalTextNode(node, + Math.round(scale * scale * 1e6) / 1e6); +}; + + +/** + * @param {Node} node Node. + * @param {ol.style.Style} style Style. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.KML.writeStyle_ = function(node, style, objectStack) { + var /** @type {ol.XmlNodeStackItem} */ context = {node: node}; + var properties = {}; + var fillStyle = style.getFill(); + var strokeStyle = style.getStroke(); + var imageStyle = style.getImage(); + var textStyle = style.getText(); + if (imageStyle instanceof ol.style.Icon) { + properties['IconStyle'] = imageStyle; + } + if (textStyle) { + properties['LabelStyle'] = textStyle; + } + if (strokeStyle) { + properties['LineStyle'] = strokeStyle; + } + if (fillStyle) { + properties['PolyStyle'] = fillStyle; + } + var parentNode = objectStack[objectStack.length - 1].node; + var orderedKeys = ol.format.KML.STYLE_SEQUENCE_[parentNode.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.STYLE_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys); +}; + + +/** + * @param {Node} node Node to append a TextNode with the Vec2 to. + * @param {ol.KMLVec2_} vec2 Vec2. + * @private + */ +ol.format.KML.writeVec2_ = function(node, vec2) { + node.setAttribute('x', vec2.x); + node.setAttribute('y', vec2.y); + node.setAttribute('xunits', vec2.xunits); + node.setAttribute('yunits', vec2.yunits); +}; + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.KML.KML_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'Document', 'Placemark' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.KML_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Document': ol.xml.makeChildAppender(ol.format.KML.writeDocument_), + 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.DOCUMENT_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Placemark': ol.xml.makeChildAppender(ol.format.KML.writePlacemark_) + }); + + +/** + * @const + * @type {Object.<string, string>} + * @private + */ +ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_ = { + 'Point': 'Point', + 'LineString': 'LineString', + 'LinearRing': 'LinearRing', + 'Polygon': 'Polygon', + 'MultiPoint': 'MultiGeometry', + 'MultiLineString': 'MultiGeometry', + 'MultiPolygon': 'MultiGeometry', + 'GeometryCollection': 'MultiGeometry' +}; + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.KML.ICON_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'href' + ], + ol.xml.makeStructureNS(ol.format.KML.GX_NAMESPACE_URIS_, [ + 'x', 'y', 'w', 'h' + ])); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.ICON_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'href': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) + }, ol.xml.makeStructureNS( + ol.format.KML.GX_NAMESPACE_URIS_, { + 'x': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'y': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'w': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'h': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode) + })); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.KML.ICON_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'scale', 'heading', 'Icon', 'hotSpot' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.ICON_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'Icon': ol.xml.makeChildAppender(ol.format.KML.writeIcon_), + 'heading': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode), + 'hotSpot': ol.xml.makeChildAppender(ol.format.KML.writeVec2_), + 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_) + }); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.KML.LABEL_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'color', 'scale' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.LABEL_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_), + 'scale': ol.xml.makeChildAppender(ol.format.KML.writeScaleTextNode_) + }); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.KML.LINE_STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'color', 'width' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.LINE_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_), + 'width': ol.xml.makeChildAppender(ol.format.XSD.writeDecimalTextNode) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.BOUNDARY_IS_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'LinearRing': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.MULTI_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'LineString': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Point': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_), + 'GeometryCollection': ol.xml.makeChildAppender( + ol.format.KML.writeMultiGeometry_) + }); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.KML.PLACEMARK_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'name', 'open', 'visibility', 'address', 'phoneNumber', 'description', + 'styleUrl', 'Style' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.PLACEMARK_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'MultiGeometry': ol.xml.makeChildAppender( + ol.format.KML.writeMultiGeometry_), + 'LineString': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'LinearRing': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Point': ol.xml.makeChildAppender( + ol.format.KML.writePrimitiveGeometry_), + 'Polygon': ol.xml.makeChildAppender(ol.format.KML.writePolygon_), + 'Style': ol.xml.makeChildAppender(ol.format.KML.writeStyle_), + 'address': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'description': ol.xml.makeChildAppender( + ol.format.XSD.writeStringTextNode), + 'name': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'open': ol.xml.makeChildAppender(ol.format.XSD.writeBooleanTextNode), + 'phoneNumber': ol.xml.makeChildAppender( + ol.format.XSD.writeStringTextNode), + 'styleUrl': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode), + 'visibility': ol.xml.makeChildAppender( + ol.format.XSD.writeBooleanTextNode) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.PRIMITIVE_GEOMETRY_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'coordinates': ol.xml.makeChildAppender( + ol.format.KML.writeCoordinatesTextNode_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.POLYGON_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'outerBoundaryIs': ol.xml.makeChildAppender( + ol.format.KML.writeBoundaryIs_), + 'innerBoundaryIs': ol.xml.makeChildAppender( + ol.format.KML.writeBoundaryIs_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.POLY_STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'color': ol.xml.makeChildAppender(ol.format.KML.writeColorTextNode_) + }); + + +/** + * @const + * @type {Object.<string, Array.<string>>} + * @private + */ +ol.format.KML.STYLE_SEQUENCE_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, [ + 'IconStyle', 'LabelStyle', 'LineStyle', 'PolyStyle' + ]); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.KML.STYLE_SERIALIZERS_ = ol.xml.makeStructureNS( + ol.format.KML.NAMESPACE_URIS_, { + 'IconStyle': ol.xml.makeChildAppender(ol.format.KML.writeIconStyle_), + 'LabelStyle': ol.xml.makeChildAppender(ol.format.KML.writeLabelStyle_), + 'LineStyle': ol.xml.makeChildAppender(ol.format.KML.writeLineStyle_), + 'PolyStyle': ol.xml.makeChildAppender(ol.format.KML.writePolyStyle_) + }); + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.KML.GX_NODE_FACTORY_ = function(value, objectStack, opt_nodeName) { + return ol.xml.createElementNS(ol.format.KML.GX_NAMESPACE_URIS_[0], + 'gx:' + opt_nodeName); +}; + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.KML.DOCUMENT_NODE_FACTORY_ = function(value, objectStack, + opt_nodeName) { + goog.asserts.assertInstanceof(value, ol.Feature, + 'value should be an ol.Feature'); + var parentNode = objectStack[objectStack.length - 1].node; + goog.asserts.assert(ol.xml.isNode(parentNode), + 'parentNode should be an XML node'); + return ol.xml.createElementNS(parentNode.namespaceURI, 'Placemark'); +}; + + +/** + * @const + * @param {*} value Value. + * @param {Array.<*>} objectStack Object stack. + * @param {string=} opt_nodeName Node name. + * @return {Node|undefined} Node. + * @private + */ +ol.format.KML.GEOMETRY_NODE_FACTORY_ = function(value, objectStack, + opt_nodeName) { + if (value) { + goog.asserts.assertInstanceof(value, ol.geom.Geometry, + 'value should be an ol.geom.Geometry'); + var parentNode = objectStack[objectStack.length - 1].node; + goog.asserts.assert(ol.xml.isNode(parentNode), + 'parentNode should be an XML node'); + return ol.xml.createElementNS(parentNode.namespaceURI, + ol.format.KML.GEOMETRY_TYPE_TO_NODENAME_[value.getType()]); + } +}; + + +/** + * A factory for creating coordinates nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.COLOR_NODE_FACTORY_ = ol.xml.makeSimpleNodeFactory('color'); + + +/** + * A factory for creating coordinates nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.COORDINATES_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('coordinates'); + + +/** + * A factory for creating innerBoundaryIs nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.INNER_BOUNDARY_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('innerBoundaryIs'); + + +/** + * A factory for creating Point nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.POINT_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('Point'); + + +/** + * A factory for creating LineString nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.LINE_STRING_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('LineString'); + + +/** + * A factory for creating LinearRing nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.LINEAR_RING_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('LinearRing'); + + +/** + * A factory for creating Polygon nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.POLYGON_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('Polygon'); + + +/** + * A factory for creating outerBoundaryIs nodes. + * @const + * @type {function(*, Array.<*>, string=): (Node|undefined)} + * @private + */ +ol.format.KML.OUTER_BOUNDARY_NODE_FACTORY_ = + ol.xml.makeSimpleNodeFactory('outerBoundaryIs'); + + +/** + * Encode an array of features in the KML format. GeometryCollections, MultiPoints, + * MultiLineStrings, and MultiPolygons are output as MultiGeometries. + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {string} Result. + * @api stable + */ +ol.format.KML.prototype.writeFeatures; + + +/** + * Encode an array of features in the KML format as an XML node. GeometryCollections, + * MultiPoints, MultiLineStrings, and MultiPolygons are output as MultiGeometries. + * + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Options. + * @return {Node} Node. + * @api + */ +ol.format.KML.prototype.writeFeaturesNode = function(features, opt_options) { + opt_options = this.adaptOptions(opt_options); + var kml = ol.xml.createElementNS(ol.format.KML.NAMESPACE_URIS_[4], 'kml'); + var xmlnsUri = 'http://www.w3.org/2000/xmlns/'; + var xmlSchemaInstanceUri = 'http://www.w3.org/2001/XMLSchema-instance'; + ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:gx', + ol.format.KML.GX_NAMESPACE_URIS_[0]); + ol.xml.setAttributeNS(kml, xmlnsUri, 'xmlns:xsi', xmlSchemaInstanceUri); + ol.xml.setAttributeNS(kml, xmlSchemaInstanceUri, 'xsi:schemaLocation', + ol.format.KML.SCHEMA_LOCATION_); + + var /** @type {ol.XmlNodeStackItem} */ context = {node: kml}; + var properties = {}; + if (features.length > 1) { + properties['Document'] = features; + } else if (features.length == 1) { + properties['Placemark'] = features[0]; + } + var orderedKeys = ol.format.KML.KML_SEQUENCE_[kml.namespaceURI]; + var values = ol.xml.makeSequence(properties, orderedKeys); + ol.xml.pushSerializeAndPop(context, ol.format.KML.KML_SERIALIZERS_, + ol.xml.OBJECT_PROPERTY_NODE_FACTORY, values, [opt_options], orderedKeys, + this); + return kml; +}; + +goog.provide('ol.ext.pbf'); +/** @typedef {function(*)} */ +ol.ext.pbf; +(function() { +var exports = {}; +var module = {exports: exports}; +var define; +/** + * @fileoverview + * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility} + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pbf = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ +exports.read = function (buffer, offset, isLE, mLen, nBytes) { + var e, m + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var nBits = -7 + var i = isLE ? (nBytes - 1) : 0 + var d = isLE ? -1 : 1 + var s = buffer[offset + i] + + i += d + + e = s & ((1 << (-nBits)) - 1) + s >>= (-nBits) + nBits += eLen + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + m = e & ((1 << (-nBits)) - 1) + e >>= (-nBits) + nBits += mLen + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8) {} + + if (e === 0) { + e = 1 - eBias + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity) + } else { + m = m + Math.pow(2, mLen) + e = e - eBias + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen) +} + +exports.write = function (buffer, value, offset, isLE, mLen, nBytes) { + var e, m, c + var eLen = nBytes * 8 - mLen - 1 + var eMax = (1 << eLen) - 1 + var eBias = eMax >> 1 + var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0) + var i = isLE ? 0 : (nBytes - 1) + var d = isLE ? 1 : -1 + var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0 + + value = Math.abs(value) + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0 + e = eMax + } else { + e = Math.floor(Math.log(value) / Math.LN2) + if (value * (c = Math.pow(2, -e)) < 1) { + e-- + c *= 2 + } + if (e + eBias >= 1) { + value += rt / c + } else { + value += rt * Math.pow(2, 1 - eBias) + } + if (value * c >= 2) { + e++ + c /= 2 + } + + if (e + eBias >= eMax) { + m = 0 + e = eMax + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen) + e = e + eBias + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen) + e = 0 + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {} + + e = (e << mLen) | m + eLen += mLen + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {} + + buffer[offset + i - d] |= s * 128 +} + +},{}],2:[function(_dereq_,module,exports){ +'use strict'; + +// lightweight Buffer shim for pbf browser build +// based on code from github.com/feross/buffer (MIT-licensed) + +module.exports = Buffer; + +var ieee754 = _dereq_('ieee754'); + +var BufferMethods; + +function Buffer(length) { + var arr; + if (length && length.length) { + arr = length; + length = arr.length; + } + var buf = new Uint8Array(length || 0); + if (arr) buf.set(arr); + + buf.readUInt32LE = BufferMethods.readUInt32LE; + buf.writeUInt32LE = BufferMethods.writeUInt32LE; + buf.readInt32LE = BufferMethods.readInt32LE; + buf.writeInt32LE = BufferMethods.writeInt32LE; + buf.readFloatLE = BufferMethods.readFloatLE; + buf.writeFloatLE = BufferMethods.writeFloatLE; + buf.readDoubleLE = BufferMethods.readDoubleLE; + buf.writeDoubleLE = BufferMethods.writeDoubleLE; + buf.toString = BufferMethods.toString; + buf.write = BufferMethods.write; + buf.slice = BufferMethods.slice; + buf.copy = BufferMethods.copy; + + buf._isBuffer = true; + return buf; +} + +var lastStr, lastStrEncoded; + +BufferMethods = { + readUInt32LE: function(pos) { + return ((this[pos]) | + (this[pos + 1] << 8) | + (this[pos + 2] << 16)) + + (this[pos + 3] * 0x1000000); + }, + + writeUInt32LE: function(val, pos) { + this[pos] = val; + this[pos + 1] = (val >>> 8); + this[pos + 2] = (val >>> 16); + this[pos + 3] = (val >>> 24); + }, + + readInt32LE: function(pos) { + return ((this[pos]) | + (this[pos + 1] << 8) | + (this[pos + 2] << 16)) + + (this[pos + 3] << 24); + }, + + readFloatLE: function(pos) { return ieee754.read(this, pos, true, 23, 4); }, + readDoubleLE: function(pos) { return ieee754.read(this, pos, true, 52, 8); }, + + writeFloatLE: function(val, pos) { return ieee754.write(this, val, pos, true, 23, 4); }, + writeDoubleLE: function(val, pos) { return ieee754.write(this, val, pos, true, 52, 8); }, + + toString: function(encoding, start, end) { + var str = '', + tmp = ''; + + start = start || 0; + end = Math.min(this.length, end || this.length); + + for (var i = start; i < end; i++) { + var ch = this[i]; + if (ch <= 0x7F) { + str += decodeURIComponent(tmp) + String.fromCharCode(ch); + tmp = ''; + } else { + tmp += '%' + ch.toString(16); + } + } + + str += decodeURIComponent(tmp); + + return str; + }, + + write: function(str, pos) { + var bytes = str === lastStr ? lastStrEncoded : encodeString(str); + for (var i = 0; i < bytes.length; i++) { + this[pos + i] = bytes[i]; + } + }, + + slice: function(start, end) { + return this.subarray(start, end); + }, + + copy: function(buf, pos) { + pos = pos || 0; + for (var i = 0; i < this.length; i++) { + buf[pos + i] = this[i]; + } + } +}; + +BufferMethods.writeInt32LE = BufferMethods.writeUInt32LE; + +Buffer.byteLength = function(str) { + lastStr = str; + lastStrEncoded = encodeString(str); + return lastStrEncoded.length; +}; + +Buffer.isBuffer = function(buf) { + return !!(buf && buf._isBuffer); +}; + +function encodeString(str) { + var length = str.length, + bytes = []; + + for (var i = 0, c, lead; i < length; i++) { + c = str.charCodeAt(i); // code point + + if (c > 0xD7FF && c < 0xE000) { + + if (lead) { + if (c < 0xDC00) { + bytes.push(0xEF, 0xBF, 0xBD); + lead = c; + continue; + + } else { + c = lead - 0xD800 << 10 | c - 0xDC00 | 0x10000; + lead = null; + } + + } else { + if (c > 0xDBFF || (i + 1 === length)) bytes.push(0xEF, 0xBF, 0xBD); + else lead = c; + + continue; + } + + } else if (lead) { + bytes.push(0xEF, 0xBF, 0xBD); + lead = null; + } + + if (c < 0x80) bytes.push(c); + else if (c < 0x800) bytes.push(c >> 0x6 | 0xC0, c & 0x3F | 0x80); + else if (c < 0x10000) bytes.push(c >> 0xC | 0xE0, c >> 0x6 & 0x3F | 0x80, c & 0x3F | 0x80); + else bytes.push(c >> 0x12 | 0xF0, c >> 0xC & 0x3F | 0x80, c >> 0x6 & 0x3F | 0x80, c & 0x3F | 0x80); + } + return bytes; +} + +},{"ieee754":1}],3:[function(_dereq_,module,exports){ +(function (global){ +'use strict'; + +module.exports = Pbf; + +var Buffer = global.Buffer || _dereq_('./buffer'); + +function Pbf(buf) { + this.buf = !Buffer.isBuffer(buf) ? new Buffer(buf || 0) : buf; + this.pos = 0; + this.length = this.buf.length; +} + +Pbf.Varint = 0; // varint: int32, int64, uint32, uint64, sint32, sint64, bool, enum +Pbf.Fixed64 = 1; // 64-bit: double, fixed64, sfixed64 +Pbf.Bytes = 2; // length-delimited: string, bytes, embedded messages, packed repeated fields +Pbf.Fixed32 = 5; // 32-bit: float, fixed32, sfixed32 + +var SHIFT_LEFT_32 = (1 << 16) * (1 << 16), + SHIFT_RIGHT_32 = 1 / SHIFT_LEFT_32, + POW_2_63 = Math.pow(2, 63); + +Pbf.prototype = { + + destroy: function() { + this.buf = null; + }, + + // === READING ================================================================= + + readFields: function(readField, result, end) { + end = end || this.length; + + while (this.pos < end) { + var val = this.readVarint(), + tag = val >> 3, + startPos = this.pos; + + readField(tag, result, this); + + if (this.pos === startPos) this.skip(val); + } + return result; + }, + + readMessage: function(readField, result) { + return this.readFields(readField, result, this.readVarint() + this.pos); + }, + + readFixed32: function() { + var val = this.buf.readUInt32LE(this.pos); + this.pos += 4; + return val; + }, + + readSFixed32: function() { + var val = this.buf.readInt32LE(this.pos); + this.pos += 4; + return val; + }, + + // 64-bit int handling is based on github.com/dpw/node-buffer-more-ints (MIT-licensed) + + readFixed64: function() { + var val = this.buf.readUInt32LE(this.pos) + this.buf.readUInt32LE(this.pos + 4) * SHIFT_LEFT_32; + this.pos += 8; + return val; + }, + + readSFixed64: function() { + var val = this.buf.readUInt32LE(this.pos) + this.buf.readInt32LE(this.pos + 4) * SHIFT_LEFT_32; + this.pos += 8; + return val; + }, + + readFloat: function() { + var val = this.buf.readFloatLE(this.pos); + this.pos += 4; + return val; + }, + + readDouble: function() { + var val = this.buf.readDoubleLE(this.pos); + this.pos += 8; + return val; + }, + + readVarint: function() { + var buf = this.buf, + val, b; + + b = buf[this.pos++]; val = b & 0x7f; if (b < 0x80) return val; + b = buf[this.pos++]; val |= (b & 0x7f) << 7; if (b < 0x80) return val; + b = buf[this.pos++]; val |= (b & 0x7f) << 14; if (b < 0x80) return val; + b = buf[this.pos++]; val |= (b & 0x7f) << 21; if (b < 0x80) return val; + + return readVarintRemainder(val, this); + }, + + readVarint64: function() { + var startPos = this.pos, + val = this.readVarint(); + + if (val < POW_2_63) return val; + + var pos = this.pos - 2; + while (this.buf[pos] === 0xff) pos--; + if (pos < startPos) pos = startPos; + + val = 0; + for (var i = 0; i < pos - startPos + 1; i++) { + var b = ~this.buf[startPos + i] & 0x7f; + val += i < 4 ? b << i * 7 : b * Math.pow(2, i * 7); + } + + return -val - 1; + }, + + readSVarint: function() { + var num = this.readVarint(); + return num % 2 === 1 ? (num + 1) / -2 : num / 2; // zigzag encoding + }, + + readBoolean: function() { + return Boolean(this.readVarint()); + }, + + readString: function() { + var end = this.readVarint() + this.pos, + str = this.buf.toString('utf8', this.pos, end); + this.pos = end; + return str; + }, + + readBytes: function() { + var end = this.readVarint() + this.pos, + buffer = this.buf.slice(this.pos, end); + this.pos = end; + return buffer; + }, + + // verbose for performance reasons; doesn't affect gzipped size + + readPackedVarint: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readVarint()); + return arr; + }, + readPackedSVarint: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readSVarint()); + return arr; + }, + readPackedBoolean: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readBoolean()); + return arr; + }, + readPackedFloat: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readFloat()); + return arr; + }, + readPackedDouble: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readDouble()); + return arr; + }, + readPackedFixed32: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readFixed32()); + return arr; + }, + readPackedSFixed32: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readSFixed32()); + return arr; + }, + readPackedFixed64: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readFixed64()); + return arr; + }, + readPackedSFixed64: function() { + var end = this.readVarint() + this.pos, arr = []; + while (this.pos < end) arr.push(this.readSFixed64()); + return arr; + }, + + skip: function(val) { + var type = val & 0x7; + if (type === Pbf.Varint) while (this.buf[this.pos++] > 0x7f) {} + else if (type === Pbf.Bytes) this.pos = this.readVarint() + this.pos; + else if (type === Pbf.Fixed32) this.pos += 4; + else if (type === Pbf.Fixed64) this.pos += 8; + else throw new Error('Unimplemented type: ' + type); + }, + + // === WRITING ================================================================= + + writeTag: function(tag, type) { + this.writeVarint((tag << 3) | type); + }, + + realloc: function(min) { + var length = this.length || 16; + + while (length < this.pos + min) length *= 2; + + if (length !== this.length) { + var buf = new Buffer(length); + this.buf.copy(buf); + this.buf = buf; + this.length = length; + } + }, + + finish: function() { + this.length = this.pos; + this.pos = 0; + return this.buf.slice(0, this.length); + }, + + writeFixed32: function(val) { + this.realloc(4); + this.buf.writeUInt32LE(val, this.pos); + this.pos += 4; + }, + + writeSFixed32: function(val) { + this.realloc(4); + this.buf.writeInt32LE(val, this.pos); + this.pos += 4; + }, + + writeFixed64: function(val) { + this.realloc(8); + this.buf.writeInt32LE(val & -1, this.pos); + this.buf.writeUInt32LE(Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); + this.pos += 8; + }, + + writeSFixed64: function(val) { + this.realloc(8); + this.buf.writeInt32LE(val & -1, this.pos); + this.buf.writeInt32LE(Math.floor(val * SHIFT_RIGHT_32), this.pos + 4); + this.pos += 8; + }, + + writeVarint: function(val) { + val = +val; + + if (val > 0xfffffff) { + writeBigVarint(val, this); + return; + } + + this.realloc(4); + + this.buf[this.pos++] = val & 0x7f | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; + this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; + this.buf[this.pos++] = ((val >>>= 7) & 0x7f) | (val > 0x7f ? 0x80 : 0); if (val <= 0x7f) return; + this.buf[this.pos++] = (val >>> 7) & 0x7f; + }, + + writeSVarint: function(val) { + this.writeVarint(val < 0 ? -val * 2 - 1 : val * 2); + }, + + writeBoolean: function(val) { + this.writeVarint(Boolean(val)); + }, + + writeString: function(str) { + str = String(str); + var bytes = Buffer.byteLength(str); + this.writeVarint(bytes); + this.realloc(bytes); + this.buf.write(str, this.pos); + this.pos += bytes; + }, + + writeFloat: function(val) { + this.realloc(4); + this.buf.writeFloatLE(val, this.pos); + this.pos += 4; + }, + + writeDouble: function(val) { + this.realloc(8); + this.buf.writeDoubleLE(val, this.pos); + this.pos += 8; + }, + + writeBytes: function(buffer) { + var len = buffer.length; + this.writeVarint(len); + this.realloc(len); + for (var i = 0; i < len; i++) this.buf[this.pos++] = buffer[i]; + }, + + writeRawMessage: function(fn, obj) { + this.pos++; // reserve 1 byte for short message length + + // write the message directly to the buffer and see how much was written + var startPos = this.pos; + fn(obj, this); + var len = this.pos - startPos; + + if (len >= 0x80) reallocForRawMessage(startPos, len, this); + + // finally, write the message length in the reserved place and restore the position + this.pos = startPos - 1; + this.writeVarint(len); + this.pos += len; + }, + + writeMessage: function(tag, fn, obj) { + this.writeTag(tag, Pbf.Bytes); + this.writeRawMessage(fn, obj); + }, + + writePackedVarint: function(tag, arr) { this.writeMessage(tag, writePackedVarint, arr); }, + writePackedSVarint: function(tag, arr) { this.writeMessage(tag, writePackedSVarint, arr); }, + writePackedBoolean: function(tag, arr) { this.writeMessage(tag, writePackedBoolean, arr); }, + writePackedFloat: function(tag, arr) { this.writeMessage(tag, writePackedFloat, arr); }, + writePackedDouble: function(tag, arr) { this.writeMessage(tag, writePackedDouble, arr); }, + writePackedFixed32: function(tag, arr) { this.writeMessage(tag, writePackedFixed32, arr); }, + writePackedSFixed32: function(tag, arr) { this.writeMessage(tag, writePackedSFixed32, arr); }, + writePackedFixed64: function(tag, arr) { this.writeMessage(tag, writePackedFixed64, arr); }, + writePackedSFixed64: function(tag, arr) { this.writeMessage(tag, writePackedSFixed64, arr); }, + + writeBytesField: function(tag, buffer) { + this.writeTag(tag, Pbf.Bytes); + this.writeBytes(buffer); + }, + writeFixed32Field: function(tag, val) { + this.writeTag(tag, Pbf.Fixed32); + this.writeFixed32(val); + }, + writeSFixed32Field: function(tag, val) { + this.writeTag(tag, Pbf.Fixed32); + this.writeSFixed32(val); + }, + writeFixed64Field: function(tag, val) { + this.writeTag(tag, Pbf.Fixed64); + this.writeFixed64(val); + }, + writeSFixed64Field: function(tag, val) { + this.writeTag(tag, Pbf.Fixed64); + this.writeSFixed64(val); + }, + writeVarintField: function(tag, val) { + this.writeTag(tag, Pbf.Varint); + this.writeVarint(val); + }, + writeSVarintField: function(tag, val) { + this.writeTag(tag, Pbf.Varint); + this.writeSVarint(val); + }, + writeStringField: function(tag, str) { + this.writeTag(tag, Pbf.Bytes); + this.writeString(str); + }, + writeFloatField: function(tag, val) { + this.writeTag(tag, Pbf.Fixed32); + this.writeFloat(val); + }, + writeDoubleField: function(tag, val) { + this.writeTag(tag, Pbf.Fixed64); + this.writeDouble(val); + }, + writeBooleanField: function(tag, val) { + this.writeVarintField(tag, Boolean(val)); + } +}; + +function readVarintRemainder(val, pbf) { + var buf = pbf.buf, b; + + b = buf[pbf.pos++]; val += (b & 0x7f) * 0x10000000; if (b < 0x80) return val; + b = buf[pbf.pos++]; val += (b & 0x7f) * 0x800000000; if (b < 0x80) return val; + b = buf[pbf.pos++]; val += (b & 0x7f) * 0x40000000000; if (b < 0x80) return val; + b = buf[pbf.pos++]; val += (b & 0x7f) * 0x2000000000000; if (b < 0x80) return val; + b = buf[pbf.pos++]; val += (b & 0x7f) * 0x100000000000000; if (b < 0x80) return val; + b = buf[pbf.pos++]; val += (b & 0x7f) * 0x8000000000000000; if (b < 0x80) return val; + + throw new Error('Expected varint not more than 10 bytes'); +} + +function writeBigVarint(val, pbf) { + pbf.realloc(10); + + var maxPos = pbf.pos + 10; + + while (val >= 1) { + if (pbf.pos >= maxPos) throw new Error('Given varint doesn\'t fit into 10 bytes'); + var b = val & 0xff; + pbf.buf[pbf.pos++] = b | (val >= 0x80 ? 0x80 : 0); + val /= 0x80; + } +} + +function reallocForRawMessage(startPos, len, pbf) { + var extraLen = + len <= 0x3fff ? 1 : + len <= 0x1fffff ? 2 : + len <= 0xfffffff ? 3 : Math.ceil(Math.log(len) / (Math.LN2 * 7)); + + // if 1 byte isn't enough for encoding message length, shift the data to the right + pbf.realloc(extraLen); + for (var i = pbf.pos - 1; i >= startPos; i--) pbf.buf[i + extraLen] = pbf.buf[i]; +} + +function writePackedVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeVarint(arr[i]); } +function writePackedSVarint(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSVarint(arr[i]); } +function writePackedFloat(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFloat(arr[i]); } +function writePackedDouble(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeDouble(arr[i]); } +function writePackedBoolean(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeBoolean(arr[i]); } +function writePackedFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed32(arr[i]); } +function writePackedSFixed32(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed32(arr[i]); } +function writePackedFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeFixed64(arr[i]); } +function writePackedSFixed64(arr, pbf) { for (var i = 0; i < arr.length; i++) pbf.writeSFixed64(arr[i]); } + +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{"./buffer":2}]},{},[3])(3) +}); +ol.ext.pbf = module.exports; +})(); + +goog.provide('ol.ext.vectortile'); +/** @typedef {function(*)} */ +ol.ext.vectortile; +(function() { +var exports = {}; +var module = {exports: exports}; +var define; +/** + * @fileoverview + * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility} + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.vectortile = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ +'use strict'; + +module.exports = Point; + +function Point(x, y) { + this.x = x; + this.y = y; +} + +Point.prototype = { + clone: function() { return new Point(this.x, this.y); }, + + add: function(p) { return this.clone()._add(p); }, + sub: function(p) { return this.clone()._sub(p); }, + mult: function(k) { return this.clone()._mult(k); }, + div: function(k) { return this.clone()._div(k); }, + rotate: function(a) { return this.clone()._rotate(a); }, + matMult: function(m) { return this.clone()._matMult(m); }, + unit: function() { return this.clone()._unit(); }, + perp: function() { return this.clone()._perp(); }, + round: function() { return this.clone()._round(); }, + + mag: function() { + return Math.sqrt(this.x * this.x + this.y * this.y); + }, + + equals: function(p) { + return this.x === p.x && + this.y === p.y; + }, + + dist: function(p) { + return Math.sqrt(this.distSqr(p)); + }, + + distSqr: function(p) { + var dx = p.x - this.x, + dy = p.y - this.y; + return dx * dx + dy * dy; + }, + + angle: function() { + return Math.atan2(this.y, this.x); + }, + + angleTo: function(b) { + return Math.atan2(this.y - b.y, this.x - b.x); + }, + + angleWith: function(b) { + return this.angleWithSep(b.x, b.y); + }, + + // Find the angle of the two vectors, solving the formula for the cross product a x b = |a||b|sin(θ) for θ. + angleWithSep: function(x, y) { + return Math.atan2( + this.x * y - this.y * x, + this.x * x + this.y * y); + }, + + _matMult: function(m) { + var x = m[0] * this.x + m[1] * this.y, + y = m[2] * this.x + m[3] * this.y; + this.x = x; + this.y = y; + return this; + }, + + _add: function(p) { + this.x += p.x; + this.y += p.y; + return this; + }, + + _sub: function(p) { + this.x -= p.x; + this.y -= p.y; + return this; + }, + + _mult: function(k) { + this.x *= k; + this.y *= k; + return this; + }, + + _div: function(k) { + this.x /= k; + this.y /= k; + return this; + }, + + _unit: function() { + this._div(this.mag()); + return this; + }, + + _perp: function() { + var y = this.y; + this.y = this.x; + this.x = -y; + return this; + }, + + _rotate: function(angle) { + var cos = Math.cos(angle), + sin = Math.sin(angle), + x = cos * this.x - sin * this.y, + y = sin * this.x + cos * this.y; + this.x = x; + this.y = y; + return this; + }, + + _round: function() { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + } +}; + +// constructs Point from an array if necessary +Point.convert = function (a) { + if (a instanceof Point) { + return a; + } + if (Array.isArray(a)) { + return new Point(a[0], a[1]); + } + return a; +}; + +},{}],2:[function(_dereq_,module,exports){ +module.exports.VectorTile = _dereq_('./lib/vectortile.js'); +module.exports.VectorTileFeature = _dereq_('./lib/vectortilefeature.js'); +module.exports.VectorTileLayer = _dereq_('./lib/vectortilelayer.js'); + +},{"./lib/vectortile.js":3,"./lib/vectortilefeature.js":4,"./lib/vectortilelayer.js":5}],3:[function(_dereq_,module,exports){ +'use strict'; + +var VectorTileLayer = _dereq_('./vectortilelayer'); + +module.exports = VectorTile; + +function VectorTile(pbf, end) { + this.layers = pbf.readFields(readTile, {}, end); +} + +function readTile(tag, layers, pbf) { + if (tag === 3) { + var layer = new VectorTileLayer(pbf, pbf.readVarint() + pbf.pos); + if (layer.length) layers[layer.name] = layer; + } +} + + +},{"./vectortilelayer":5}],4:[function(_dereq_,module,exports){ +'use strict'; + +var Point = _dereq_('point-geometry'); + +module.exports = VectorTileFeature; + +function VectorTileFeature(pbf, end, extent, keys, values) { + // Public + this.properties = {}; + this.extent = extent; + this.type = 0; + + // Private + this._pbf = pbf; + this._geometry = -1; + this._keys = keys; + this._values = values; + + pbf.readFields(readFeature, this, end); +} + +function readFeature(tag, feature, pbf) { + if (tag == 1) feature._id = pbf.readVarint(); + else if (tag == 2) readTag(pbf, feature); + else if (tag == 3) feature.type = pbf.readVarint(); + else if (tag == 4) feature._geometry = pbf.pos; +} + +function readTag(pbf, feature) { + var end = pbf.readVarint() + pbf.pos; + + while (pbf.pos < end) { + var key = feature._keys[pbf.readVarint()], + value = feature._values[pbf.readVarint()]; + feature.properties[key] = value; + } +} + +VectorTileFeature.types = ['Unknown', 'Point', 'LineString', 'Polygon']; + +VectorTileFeature.prototype.loadGeometry = function() { + var pbf = this._pbf; + pbf.pos = this._geometry; + + var end = pbf.readVarint() + pbf.pos, + cmd = 1, + length = 0, + x = 0, + y = 0, + lines = [], + line; + + while (pbf.pos < end) { + if (!length) { + var cmdLen = pbf.readVarint(); + cmd = cmdLen & 0x7; + length = cmdLen >> 3; + } + + length--; + + if (cmd === 1 || cmd === 2) { + x += pbf.readSVarint(); + y += pbf.readSVarint(); + + if (cmd === 1) { // moveTo + if (line) lines.push(line); + line = []; + } + + line.push(new Point(x, y)); + + } else if (cmd === 7) { + + // Workaround for https://github.com/mapbox/mapnik-vector-tile/issues/90 + if (line) { + line.push(line[0].clone()); // closePolygon + } + + } else { + throw new Error('unknown command ' + cmd); + } + } + + if (line) lines.push(line); + + return lines; +}; + +VectorTileFeature.prototype.bbox = function() { + var pbf = this._pbf; + pbf.pos = this._geometry; + + var end = pbf.readVarint() + pbf.pos, + cmd = 1, + length = 0, + x = 0, + y = 0, + x1 = Infinity, + x2 = -Infinity, + y1 = Infinity, + y2 = -Infinity; + + while (pbf.pos < end) { + if (!length) { + var cmdLen = pbf.readVarint(); + cmd = cmdLen & 0x7; + length = cmdLen >> 3; + } + + length--; + + if (cmd === 1 || cmd === 2) { + x += pbf.readSVarint(); + y += pbf.readSVarint(); + if (x < x1) x1 = x; + if (x > x2) x2 = x; + if (y < y1) y1 = y; + if (y > y2) y2 = y; + + } else if (cmd !== 7) { + throw new Error('unknown command ' + cmd); + } + } + + return [x1, y1, x2, y2]; +}; + +VectorTileFeature.prototype.toGeoJSON = function(x, y, z) { + var size = this.extent * Math.pow(2, z), + x0 = this.extent * x, + y0 = this.extent * y, + coords = this.loadGeometry(), + type = VectorTileFeature.types[this.type], + i, j; + + function project(line) { + for (var j = 0; j < line.length; j++) { + var p = line[j], y2 = 180 - (p.y + y0) * 360 / size; + line[j] = [ + (p.x + x0) * 360 / size - 180, + 360 / Math.PI * Math.atan(Math.exp(y2 * Math.PI / 180)) - 90 + ]; + } + } + + switch (this.type) { + case 1: + var points = []; + for (i = 0; i < coords.length; i++) { + points[i] = coords[i][0]; + } + coords = points; + project(coords); + break; + + case 2: + for (i = 0; i < coords.length; i++) { + project(coords[i]); + } + break; + + case 3: + coords = classifyRings(coords); + for (i = 0; i < coords.length; i++) { + for (j = 0; j < coords[i].length; j++) { + project(coords[i][j]); + } + } + break; + } + + if (coords.length === 1) { + coords = coords[0]; + } else { + type = 'Multi' + type; + } + + var result = { + type: "Feature", + geometry: { + type: type, + coordinates: coords + }, + properties: this.properties + }; + + if ('_id' in this) { + result.id = this._id; + } + + return result; +}; + +// classifies an array of rings into polygons with outer rings and holes + +function classifyRings(rings) { + var len = rings.length; + + if (len <= 1) return [rings]; + + var polygons = [], + polygon, + ccw; + + for (var i = 0; i < len; i++) { + var area = signedArea(rings[i]); + if (area === 0) continue; + + if (ccw === undefined) ccw = area < 0; + + if (ccw === area < 0) { + if (polygon) polygons.push(polygon); + polygon = [rings[i]]; + + } else { + polygon.push(rings[i]); + } + } + if (polygon) polygons.push(polygon); + + return polygons; +} + +function signedArea(ring) { + var sum = 0; + for (var i = 0, len = ring.length, j = len - 1, p1, p2; i < len; j = i++) { + p1 = ring[i]; + p2 = ring[j]; + sum += (p2.x - p1.x) * (p1.y + p2.y); + } + return sum; +} + +},{"point-geometry":1}],5:[function(_dereq_,module,exports){ +'use strict'; + +var VectorTileFeature = _dereq_('./vectortilefeature.js'); + +module.exports = VectorTileLayer; + +function VectorTileLayer(pbf, end) { + // Public + this.version = 1; + this.name = null; + this.extent = 4096; + this.length = 0; + + // Private + this._pbf = pbf; + this._keys = []; + this._values = []; + this._features = []; + + pbf.readFields(readLayer, this, end); + + this.length = this._features.length; +} + +function readLayer(tag, layer, pbf) { + if (tag === 15) layer.version = pbf.readVarint(); + else if (tag === 1) layer.name = pbf.readString(); + else if (tag === 5) layer.extent = pbf.readVarint(); + else if (tag === 2) layer._features.push(pbf.pos); + else if (tag === 3) layer._keys.push(pbf.readString()); + else if (tag === 4) layer._values.push(readValueMessage(pbf)); +} + +function readValueMessage(pbf) { + var value = null, + end = pbf.readVarint() + pbf.pos; + + while (pbf.pos < end) { + var tag = pbf.readVarint() >> 3; + + value = tag === 1 ? pbf.readString() : + tag === 2 ? pbf.readFloat() : + tag === 3 ? pbf.readDouble() : + tag === 4 ? pbf.readVarint64() : + tag === 5 ? pbf.readVarint() : + tag === 6 ? pbf.readSVarint() : + tag === 7 ? pbf.readBoolean() : null; + } + + return value; +} + +// return feature `i` from this layer as a `VectorTileFeature` +VectorTileLayer.prototype.feature = function(i) { + if (i < 0 || i >= this._features.length) throw new Error('feature index out of bounds'); + + this._pbf.pos = this._features[i]; + + var end = this._pbf.readVarint() + this._pbf.pos; + return new VectorTileFeature(this._pbf, end, this.extent, this._keys, this._values); +}; + +},{"./vectortilefeature.js":4}]},{},[2])(2) +}); +ol.ext.vectortile = module.exports; +})(); + +//FIXME Implement projection handling + +goog.provide('ol.format.MVT'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.ext.pbf'); +goog.require('ol.ext.vectortile'); +goog.require('ol.format.Feature'); +goog.require('ol.format.FormatType'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.proj'); +goog.require('ol.proj.Projection'); +goog.require('ol.proj.Units'); +goog.require('ol.render.Feature'); + + +/** + * @classdesc + * Feature format for reading data in the Mapbox MVT format. + * + * @constructor + * @extends {ol.format.Feature} + * @param {olx.format.MVTOptions=} opt_options Options. + * @api + */ +ol.format.MVT = function(opt_options) { + + ol.format.Feature.call(this); + + var options = opt_options ? opt_options : {}; + + /** + * @type {ol.proj.Projection} + */ + this.defaultDataProjection = new ol.proj.Projection({ + code: '', + units: ol.proj.Units.TILE_PIXELS + }); + + /** + * @private + * @type {function((ol.geom.Geometry|Object.<string, *>)=)| + * function(ol.geom.GeometryType,Array.<number>, + * (Array.<number>|Array.<Array.<number>>),Object.<string, *>)} + */ + this.featureClass_ = options.featureClass ? + options.featureClass : ol.render.Feature; + + /** + * @private + * @type {string} + */ + this.geometryName_ = options.geometryName ? + options.geometryName : 'geometry'; + + /** + * @private + * @type {string} + */ + this.layerName_ = options.layerName ? options.layerName : 'layer'; + + /** + * @private + * @type {Array.<string>} + */ + this.layers_ = options.layers ? options.layers : null; + +}; +ol.inherits(ol.format.MVT, ol.format.Feature); + + +/** + * @inheritDoc + */ +ol.format.MVT.prototype.getType = function() { + return ol.format.FormatType.ARRAY_BUFFER; +}; + + +/** + * @private + * @param {Object} rawFeature Raw Mapbox feature. + * @param {string} layer Layer. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + */ +ol.format.MVT.prototype.readFeature_ = function( + rawFeature, layer, opt_options) { + var feature = new this.featureClass_(); + var values = rawFeature.properties; + values[this.layerName_] = layer; + var geometry = ol.format.Feature.transformWithOptions( + ol.format.MVT.readGeometry_(rawFeature), false, + this.adaptOptions(opt_options)); + if (geometry) { + goog.asserts.assertInstanceof(geometry, ol.geom.Geometry); + values[this.geometryName_] = geometry; + } + feature.setProperties(values); + feature.setGeometryName(this.geometryName_); + return feature; +}; + + +/** + * @private + * @param {Object} rawFeature Raw Mapbox feature. + * @param {string} layer Layer. + * @return {ol.render.Feature} Feature. + */ +ol.format.MVT.prototype.readRenderFeature_ = function(rawFeature, layer) { + var coords = rawFeature.loadGeometry(); + var ends = []; + var flatCoordinates = []; + ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends); + + var type = rawFeature.type; + /** @type {ol.geom.GeometryType} */ + var geometryType; + if (type === 1) { + geometryType = coords.length === 1 ? + ol.geom.GeometryType.POINT : ol.geom.GeometryType.MULTI_POINT; + } else if (type === 2) { + if (coords.length === 1) { + geometryType = ol.geom.GeometryType.LINE_STRING; + } else { + geometryType = ol.geom.GeometryType.MULTI_LINE_STRING; + } + } else if (type === 3) { + geometryType = ol.geom.GeometryType.POLYGON; + } + + var values = rawFeature.properties; + values[this.layerName_] = layer; + + return new this.featureClass_(geometryType, flatCoordinates, ends, values); +}; + + +/** + * @inheritDoc + * @api + */ +ol.format.MVT.prototype.readFeatures = function(source, opt_options) { + goog.asserts.assertInstanceof(source, ArrayBuffer); + + var layers = this.layers_; + + var pbf = new ol.ext.pbf(source); + var tile = new ol.ext.vectortile.VectorTile(pbf); + var features = []; + var featureClass = this.featureClass_; + var layer, feature; + for (var name in tile.layers) { + if (layers && layers.indexOf(name) == -1) { + continue; + } + layer = tile.layers[name]; + + for (var i = 0, ii = layer.length; i < ii; ++i) { + if (featureClass === ol.render.Feature) { + feature = this.readRenderFeature_(layer.feature(i), name); + } else { + feature = this.readFeature_(layer.feature(i), name, opt_options); + } + features.push(feature); + } + } + + return features; +}; + + +/** + * @inheritDoc + * @api + */ +ol.format.MVT.prototype.readProjection = function(source) { + return this.defaultDataProjection; +}; + + +/** + * Sets the layers that features will be read from. + * @param {Array.<string>} layers Layers. + * @api + */ +ol.format.MVT.prototype.setLayers = function(layers) { + this.layers_ = layers; +}; + + +/** + * @private + * @param {Object} coords Raw feature coordinates. + * @param {Array.<number>} flatCoordinates Flat coordinates to be populated by + * this function. + * @param {Array.<number>} ends Ends to be populated by this function. + */ +ol.format.MVT.calculateFlatCoordinates_ = function( + coords, flatCoordinates, ends) { + var end = 0; + for (var i = 0, ii = coords.length; i < ii; ++i) { + var line = coords[i]; + var j, jj; + for (j = 0, jj = line.length; j < jj; ++j) { + var coord = line[j]; + // Non-tilespace coords can be calculated here when a TileGrid and + // TileCoord are known. + flatCoordinates.push(coord.x, coord.y); + } + end += 2 * j; + ends.push(end); + } +}; + + +/** + * @private + * @param {Object} rawFeature Raw Mapbox feature. + * @return {ol.geom.Geometry} Geometry. + */ +ol.format.MVT.readGeometry_ = function(rawFeature) { + var type = rawFeature.type; + if (type === 0) { + return null; + } + + var coords = rawFeature.loadGeometry(); + var ends = []; + var flatCoordinates = []; + ol.format.MVT.calculateFlatCoordinates_(coords, flatCoordinates, ends); + + var geom; + if (type === 1) { + geom = coords.length === 1 ? + new ol.geom.Point(null) : new ol.geom.MultiPoint(null); + } else if (type === 2) { + if (coords.length === 1) { + geom = new ol.geom.LineString(null); + } else { + geom = new ol.geom.MultiLineString(null); + } + } else if (type === 3) { + geom = new ol.geom.Polygon(null); + } + + geom.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates, + ends); + + return geom; +}; + +goog.provide('ol.format.ogc.filter'); +goog.provide('ol.format.ogc.filter.Filter'); +goog.provide('ol.format.ogc.filter.Logical'); +goog.provide('ol.format.ogc.filter.LogicalBinary'); +goog.provide('ol.format.ogc.filter.And'); +goog.provide('ol.format.ogc.filter.Or'); +goog.provide('ol.format.ogc.filter.Not'); +goog.provide('ol.format.ogc.filter.Bbox'); +goog.provide('ol.format.ogc.filter.Comparison'); +goog.provide('ol.format.ogc.filter.ComparisonBinary'); +goog.provide('ol.format.ogc.filter.EqualTo'); +goog.provide('ol.format.ogc.filter.NotEqualTo'); +goog.provide('ol.format.ogc.filter.LessThan'); +goog.provide('ol.format.ogc.filter.LessThanOrEqualTo'); +goog.provide('ol.format.ogc.filter.GreaterThan'); +goog.provide('ol.format.ogc.filter.GreaterThanOrEqualTo'); +goog.provide('ol.format.ogc.filter.IsNull'); +goog.provide('ol.format.ogc.filter.IsBetween'); +goog.provide('ol.format.ogc.filter.IsLike'); + + +/** + * Create a logical `<And>` operator between two filter conditions. + * + * @param {!ol.format.ogc.filter.Filter} conditionA First filter condition. + * @param {!ol.format.ogc.filter.Filter} conditionB Second filter condition. + * @returns {!ol.format.ogc.filter.And} `<And>` operator. + * @api + */ +ol.format.ogc.filter.and = function(conditionA, conditionB) { + return new ol.format.ogc.filter.And(conditionA, conditionB); +}; + + +/** + * Create a logical `<Or>` operator between two filter conditions. + * + * @param {!ol.format.ogc.filter.Filter} conditionA First filter condition. + * @param {!ol.format.ogc.filter.Filter} conditionB Second filter condition. + * @returns {!ol.format.ogc.filter.Or} `<Or>` operator. + * @api + */ +ol.format.ogc.filter.or = function(conditionA, conditionB) { + return new ol.format.ogc.filter.Or(conditionA, conditionB); +}; + + +/** + * Represents a logical `<Not>` operator for a filter condition. + * + * @param {!ol.format.ogc.filter.Filter} condition Filter condition. + * @returns {!ol.format.ogc.filter.Not} `<Not>` operator. + * @api + */ +ol.format.ogc.filter.not = function(condition) { + return new ol.format.ogc.filter.Not(condition); +}; + + +/** + * Create a `<BBOX>` operator to test whether a geometry-valued property + * intersects a fixed bounding box + * + * @param {!string} geometryName Geometry name to use. + * @param {!ol.Extent} extent Extent. + * @param {string=} opt_srsName SRS name. No srsName attribute will be + * set on geometries when this is not provided. + * @returns {!ol.format.ogc.filter.Bbox} `<BBOX>` operator. + * @api + */ +ol.format.ogc.filter.bbox = function(geometryName, extent, opt_srsName) { + return new ol.format.ogc.filter.Bbox(geometryName, extent, opt_srsName); +}; + + +/** + * Creates a `<PropertyIsEqualTo>` comparison operator. + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + * @returns {!ol.format.ogc.filter.EqualTo} `<PropertyIsEqualTo>` operator. + * @api + */ +ol.format.ogc.filter.equalTo = function(propertyName, expression, opt_matchCase) { + return new ol.format.ogc.filter.EqualTo(propertyName, expression, opt_matchCase); +}; + + +/** + * Creates a `<PropertyIsNotEqualTo>` comparison operator. + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + * @returns {!ol.format.ogc.filter.NotEqualTo} `<PropertyIsNotEqualTo>` operator. + * @api + */ +ol.format.ogc.filter.notEqualTo = function(propertyName, expression, opt_matchCase) { + return new ol.format.ogc.filter.NotEqualTo(propertyName, expression, opt_matchCase); +}; + + +/** + * Creates a `<PropertyIsLessThan>` comparison operator. + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @returns {!ol.format.ogc.filter.LessThan} `<PropertyIsLessThan>` operator. + * @api + */ +ol.format.ogc.filter.lessThan = function(propertyName, expression) { + return new ol.format.ogc.filter.LessThan(propertyName, expression); +}; + + +/** + * Creates a `<PropertyIsLessThanOrEqualTo>` comparison operator. + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @returns {!ol.format.ogc.filter.LessThanOrEqualTo} `<PropertyIsLessThanOrEqualTo>` operator. + * @api + */ +ol.format.ogc.filter.lessThanOrEqualTo = function(propertyName, expression) { + return new ol.format.ogc.filter.LessThanOrEqualTo(propertyName, expression); +}; + + +/** + * Creates a `<PropertyIsGreaterThan>` comparison operator. + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @returns {!ol.format.ogc.filter.GreaterThan} `<PropertyIsGreaterThan>` operator. + * @api + */ +ol.format.ogc.filter.greaterThan = function(propertyName, expression) { + return new ol.format.ogc.filter.GreaterThan(propertyName, expression); +}; + + +/** + * Creates a `<PropertyIsGreaterThanOrEqualTo>` comparison operator. + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @returns {!ol.format.ogc.filter.GreaterThanOrEqualTo} `<PropertyIsGreaterThanOrEqualTo>` operator. + * @api + */ +ol.format.ogc.filter.greaterThanOrEqualTo = function(propertyName, expression) { + return new ol.format.ogc.filter.GreaterThanOrEqualTo(propertyName, expression); +}; + + +/** + * Creates a `<PropertyIsNull>` comparison operator to test whether a property value + * is null. + * + * @param {!string} propertyName Name of the context property to compare. + * @returns {!ol.format.ogc.filter.IsNull} `<PropertyIsNull>` operator. + * @api + */ +ol.format.ogc.filter.isNull = function(propertyName) { + return new ol.format.ogc.filter.IsNull(propertyName); +}; + + +/** + * Creates a `<PropertyIsBetween>` comparison operator to test whether an expression + * value lies within a range given by a lower and upper bound (inclusive). + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} lowerBoundary The lower bound of the range. + * @param {!number} upperBoundary The upper bound of the range. + * @returns {!ol.format.ogc.filter.IsBetween} `<PropertyIsBetween>` operator. + * @api + */ +ol.format.ogc.filter.between = function(propertyName, lowerBoundary, upperBoundary) { + return new ol.format.ogc.filter.IsBetween(propertyName, lowerBoundary, upperBoundary); +}; + + +/** + * Represents a `<PropertyIsLike>` comparison operator that matches a string property + * value against a text pattern. + * + * @param {!string} propertyName Name of the context property to compare. + * @param {!string} pattern Text pattern. + * @param {string=} opt_wildCard Pattern character which matches any sequence of + * zero or more string characters. Default is '*'. + * @param {string=} opt_singleChar pattern character which matches any single + * string character. Default is '.'. + * @param {string=} opt_escapeChar Escape character which can be used to escape + * the pattern characters. Default is '!'. + * @param {boolean=} opt_matchCase Case-sensitive? + * @returns {!ol.format.ogc.filter.IsLike} `<PropertyIsLike>` operator. + * @api + */ +ol.format.ogc.filter.like = function(propertyName, pattern, + opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) { + return new ol.format.ogc.filter.IsLike(propertyName, pattern, + opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase); +}; + + +/** + * @classdesc + * Abstract class; normally only used for creating subclasses and not instantiated in apps. + * Base class for WFS GetFeature filters. + * + * @constructor + * @param {!string} tagName The XML tag name for this filter. + * @struct + * @api + */ +ol.format.ogc.filter.Filter = function(tagName) { + + /** + * @private + * @type {!string} + */ + this.tagName_ = tagName; +}; + +/** + * The XML tag name for a filter. + * @returns {!string} Name. + */ +ol.format.ogc.filter.Filter.prototype.getTagName = function() { + return this.tagName_; +}; + + +// Logical filters + + +/** + * @classdesc + * Abstract class; normally only used for creating subclasses and not instantiated in apps. + * Base class for WFS GetFeature logical filters. + * + * @constructor + * @param {!string} tagName The XML tag name for this filter. + * @extends {ol.format.ogc.filter.Filter} + */ +ol.format.ogc.filter.Logical = function(tagName) { + ol.format.ogc.filter.Filter.call(this, tagName); +}; +ol.inherits(ol.format.ogc.filter.Logical, ol.format.ogc.filter.Filter); + + +/** + * @classdesc + * Abstract class; normally only used for creating subclasses and not instantiated in apps. + * Base class for WFS GetFeature binary logical filters. + * + * @constructor + * @param {!string} tagName The XML tag name for this filter. + * @param {!ol.format.ogc.filter.Filter} conditionA First filter condition. + * @param {!ol.format.ogc.filter.Filter} conditionB Second filter condition. + * @extends {ol.format.ogc.filter.Logical} + */ +ol.format.ogc.filter.LogicalBinary = function(tagName, conditionA, conditionB) { + + ol.format.ogc.filter.Logical.call(this, tagName); + + /** + * @public + * @type {!ol.format.ogc.filter.Filter} + */ + this.conditionA = conditionA; + + /** + * @public + * @type {!ol.format.ogc.filter.Filter} + */ + this.conditionB = conditionB; + +}; +ol.inherits(ol.format.ogc.filter.LogicalBinary, ol.format.ogc.filter.Logical); + + +/** + * @classdesc + * Represents a logical `<And>` operator between two filter conditions. + * + * @constructor + * @param {!ol.format.ogc.filter.Filter} conditionA First filter condition. + * @param {!ol.format.ogc.filter.Filter} conditionB Second filter condition. + * @extends {ol.format.ogc.filter.LogicalBinary} + * @api + */ +ol.format.ogc.filter.And = function(conditionA, conditionB) { + ol.format.ogc.filter.LogicalBinary.call(this, 'And', conditionA, conditionB); +}; +ol.inherits(ol.format.ogc.filter.And, ol.format.ogc.filter.LogicalBinary); + + +/** + * @classdesc + * Represents a logical `<Or>` operator between two filter conditions. + * + * @constructor + * @param {!ol.format.ogc.filter.Filter} conditionA First filter condition. + * @param {!ol.format.ogc.filter.Filter} conditionB Second filter condition. + * @extends {ol.format.ogc.filter.LogicalBinary} + * @api + */ +ol.format.ogc.filter.Or = function(conditionA, conditionB) { + ol.format.ogc.filter.LogicalBinary.call(this, 'Or', conditionA, conditionB); +}; +ol.inherits(ol.format.ogc.filter.Or, ol.format.ogc.filter.LogicalBinary); + + +/** + * @classdesc + * Represents a logical `<Not>` operator for a filter condition. + * + * @constructor + * @param {!ol.format.ogc.filter.Filter} condition Filter condition. + * @extends {ol.format.ogc.filter.Logical} + * @api + */ +ol.format.ogc.filter.Not = function(condition) { + + ol.format.ogc.filter.Logical.call(this, 'Not'); + + /** + * @public + * @type {!ol.format.ogc.filter.Filter} + */ + this.condition = condition; +}; +ol.inherits(ol.format.ogc.filter.Not, ol.format.ogc.filter.Logical); + + +// Spatial filters + + +/** + * @classdesc + * Represents a `<BBOX>` operator to test whether a geometry-valued property + * intersects a fixed bounding box + * + * @constructor + * @param {!string} geometryName Geometry name to use. + * @param {!ol.Extent} extent Extent. + * @param {string=} opt_srsName SRS name. No srsName attribute will be + * set on geometries when this is not provided. + * @extends {ol.format.ogc.filter.Filter} + * @api + */ +ol.format.ogc.filter.Bbox = function(geometryName, extent, opt_srsName) { + + ol.format.ogc.filter.Filter.call(this, 'BBOX'); + + /** + * @public + * @type {!string} + */ + this.geometryName = geometryName; + + /** + * @public + * @type {ol.Extent} + */ + this.extent = extent; + + /** + * @public + * @type {string|undefined} + */ + this.srsName = opt_srsName; +}; +ol.inherits(ol.format.ogc.filter.Bbox, ol.format.ogc.filter.Filter); + + +// Property comparison filters + + +/** + * @classdesc + * Abstract class; normally only used for creating subclasses and not instantiated in apps. + * Base class for WFS GetFeature property comparison filters. + * + * @constructor + * @param {!string} tagName The XML tag name for this filter. + * @param {!string} propertyName Name of the context property to compare. + * @extends {ol.format.ogc.filter.Filter} + * @api + */ +ol.format.ogc.filter.Comparison = function(tagName, propertyName) { + + ol.format.ogc.filter.Filter.call(this, tagName); + + /** + * @public + * @type {!string} + */ + this.propertyName = propertyName; +}; +ol.inherits(ol.format.ogc.filter.Comparison, ol.format.ogc.filter.Filter); + + +/** + * @classdesc + * Abstract class; normally only used for creating subclasses and not instantiated in apps. + * Base class for WFS GetFeature property binary comparison filters. + * + * @constructor + * @param {!string} tagName The XML tag name for this filter. + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + * @extends {ol.format.ogc.filter.Comparison} + * @api + */ +ol.format.ogc.filter.ComparisonBinary = function( + tagName, propertyName, expression, opt_matchCase) { + + ol.format.ogc.filter.Comparison.call(this, tagName, propertyName); + + /** + * @public + * @type {!(string|number)} + */ + this.expression = expression; + + /** + * @public + * @type {boolean|undefined} + */ + this.matchCase = opt_matchCase; +}; +ol.inherits(ol.format.ogc.filter.ComparisonBinary, ol.format.ogc.filter.Comparison); + + +/** + * @classdesc + * Represents a `<PropertyIsEqualTo>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + * @extends {ol.format.ogc.filter.ComparisonBinary} + * @api + */ +ol.format.ogc.filter.EqualTo = function(propertyName, expression, opt_matchCase) { + ol.format.ogc.filter.ComparisonBinary.call(this, 'PropertyIsEqualTo', propertyName, expression, opt_matchCase); +}; +ol.inherits(ol.format.ogc.filter.EqualTo, ol.format.ogc.filter.ComparisonBinary); + + +/** + * @classdesc + * Represents a `<PropertyIsNotEqualTo>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!(string|number)} expression The value to compare. + * @param {boolean=} opt_matchCase Case-sensitive? + * @extends {ol.format.ogc.filter.ComparisonBinary} + * @api + */ +ol.format.ogc.filter.NotEqualTo = function(propertyName, expression, opt_matchCase) { + ol.format.ogc.filter.ComparisonBinary.call(this, 'PropertyIsNotEqualTo', propertyName, expression, opt_matchCase); +}; +ol.inherits(ol.format.ogc.filter.NotEqualTo, ol.format.ogc.filter.ComparisonBinary); + + +/** + * @classdesc + * Represents a `<PropertyIsLessThan>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @extends {ol.format.ogc.filter.ComparisonBinary} + * @api + */ +ol.format.ogc.filter.LessThan = function(propertyName, expression) { + ol.format.ogc.filter.ComparisonBinary.call(this, 'PropertyIsLessThan', propertyName, expression); +}; +ol.inherits(ol.format.ogc.filter.LessThan, ol.format.ogc.filter.ComparisonBinary); + + +/** + * @classdesc + * Represents a `<PropertyIsLessThanOrEqualTo>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @extends {ol.format.ogc.filter.ComparisonBinary} + * @api + */ +ol.format.ogc.filter.LessThanOrEqualTo = function(propertyName, expression) { + ol.format.ogc.filter.ComparisonBinary.call(this, 'PropertyIsLessThanOrEqualTo', propertyName, expression); +}; +ol.inherits(ol.format.ogc.filter.LessThanOrEqualTo, ol.format.ogc.filter.ComparisonBinary); + + +/** + * @classdesc + * Represents a `<PropertyIsGreaterThan>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @extends {ol.format.ogc.filter.ComparisonBinary} + * @api + */ +ol.format.ogc.filter.GreaterThan = function(propertyName, expression) { + ol.format.ogc.filter.ComparisonBinary.call(this, 'PropertyIsGreaterThan', propertyName, expression); +}; +ol.inherits(ol.format.ogc.filter.GreaterThan, ol.format.ogc.filter.ComparisonBinary); + + +/** + * @classdesc + * Represents a `<PropertyIsGreaterThanOrEqualTo>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} expression The value to compare. + * @extends {ol.format.ogc.filter.ComparisonBinary} + * @api + */ +ol.format.ogc.filter.GreaterThanOrEqualTo = function(propertyName, expression) { + ol.format.ogc.filter.ComparisonBinary.call(this, 'PropertyIsGreaterThanOrEqualTo', propertyName, expression); +}; +ol.inherits(ol.format.ogc.filter.GreaterThanOrEqualTo, ol.format.ogc.filter.ComparisonBinary); + + +/** + * @classdesc + * Represents a `<PropertyIsNull>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @extends {ol.format.ogc.filter.Comparison} + * @api + */ +ol.format.ogc.filter.IsNull = function(propertyName) { + ol.format.ogc.filter.Comparison.call(this, 'PropertyIsNull', propertyName); +}; +ol.inherits(ol.format.ogc.filter.IsNull, ol.format.ogc.filter.Comparison); + + +/** + * @classdesc + * Represents a `<PropertyIsBetween>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!number} lowerBoundary The lower bound of the range. + * @param {!number} upperBoundary The upper bound of the range. + * @extends {ol.format.ogc.filter.Comparison} + * @api + */ +ol.format.ogc.filter.IsBetween = function(propertyName, lowerBoundary, upperBoundary) { + ol.format.ogc.filter.Comparison.call(this, 'PropertyIsBetween', propertyName); + + /** + * @public + * @type {!number} + */ + this.lowerBoundary = lowerBoundary; + + /** + * @public + * @type {!number} + */ + this.upperBoundary = upperBoundary; +}; +ol.inherits(ol.format.ogc.filter.IsBetween, ol.format.ogc.filter.Comparison); + + +/** + * @classdesc + * Represents a `<PropertyIsLike>` comparison operator. + * + * @constructor + * @param {!string} propertyName Name of the context property to compare. + * @param {!string} pattern Text pattern. + * @param {string=} opt_wildCard Pattern character which matches any sequence of + * zero or more string characters. Default is '*'. + * @param {string=} opt_singleChar pattern character which matches any single + * string character. Default is '.'. + * @param {string=} opt_escapeChar Escape character which can be used to escape + * the pattern characters. Default is '!'. + * @param {boolean=} opt_matchCase Case-sensitive? + * @extends {ol.format.ogc.filter.Comparison} + * @api + */ +ol.format.ogc.filter.IsLike = function(propertyName, pattern, + opt_wildCard, opt_singleChar, opt_escapeChar, opt_matchCase) { + ol.format.ogc.filter.Comparison.call(this, 'PropertyIsLike', propertyName); + + /** + * @public + * @type {!string} + */ + this.pattern = pattern; + + /** + * @public + * @type {!string} + */ + this.wildCard = (opt_wildCard !== undefined) ? opt_wildCard : '*'; + + /** + * @public + * @type {!string} + */ + this.singleChar = (opt_singleChar !== undefined) ? opt_singleChar : '.'; + + /** + * @public + * @type {!string} + */ + this.escapeChar = (opt_escapeChar !== undefined) ? opt_escapeChar : '!'; + + /** + * @public + * @type {boolean|undefined} + */ + this.matchCase = opt_matchCase; +}; +ol.inherits(ol.format.ogc.filter.IsLike, ol.format.ogc.filter.Comparison); + +// FIXME add typedef for stack state objects +goog.provide('ol.format.OSMXML'); + +goog.require('goog.asserts'); +goog.require('ol.array'); +goog.require('ol.Feature'); +goog.require('ol.format.Feature'); +goog.require('ol.format.XMLFeature'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Feature format for reading data in the + * [OSMXML format](http://wiki.openstreetmap.org/wiki/OSM_XML). + * + * @constructor + * @extends {ol.format.XMLFeature} + * @api stable + */ +ol.format.OSMXML = function() { + ol.format.XMLFeature.call(this); + + /** + * @inheritDoc + */ + this.defaultDataProjection = ol.proj.get('EPSG:4326'); +}; +ol.inherits(ol.format.OSMXML, ol.format.XMLFeature); + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.OSMXML.EXTENSIONS_ = ['.osm']; + + +/** + * @inheritDoc + */ +ol.format.OSMXML.prototype.getExtensions = function() { + return ol.format.OSMXML.EXTENSIONS_; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.OSMXML.readNode_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'node', 'localName should be node'); + var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); + var state = /** @type {Object} */ (objectStack[objectStack.length - 1]); + var id = node.getAttribute('id'); + /** @type {ol.Coordinate} */ + var coordinates = [ + parseFloat(node.getAttribute('lon')), + parseFloat(node.getAttribute('lat')) + ]; + state.nodes[id] = coordinates; + + var values = ol.xml.pushParseAndPop({ + tags: {} + }, ol.format.OSMXML.NODE_PARSERS_, node, objectStack); + if (!ol.object.isEmpty(values.tags)) { + var geometry = new ol.geom.Point(coordinates); + ol.format.Feature.transformWithOptions(geometry, false, options); + var feature = new ol.Feature(geometry); + feature.setId(id); + feature.setProperties(values.tags); + state.features.push(feature); + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.OSMXML.readWay_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'way', 'localName should be way'); + var options = /** @type {olx.format.ReadOptions} */ (objectStack[0]); + var id = node.getAttribute('id'); + var values = ol.xml.pushParseAndPop({ + ndrefs: [], + tags: {} + }, ol.format.OSMXML.WAY_PARSERS_, node, objectStack); + var state = /** @type {Object} */ (objectStack[objectStack.length - 1]); + /** @type {Array.<number>} */ + var flatCoordinates = []; + for (var i = 0, ii = values.ndrefs.length; i < ii; i++) { + var point = state.nodes[values.ndrefs[i]]; + ol.array.extend(flatCoordinates, point); + } + var geometry; + if (values.ndrefs[0] == values.ndrefs[values.ndrefs.length - 1]) { + // closed way + geometry = new ol.geom.Polygon(null); + geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates, + [flatCoordinates.length]); + } else { + geometry = new ol.geom.LineString(null); + geometry.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates); + } + ol.format.Feature.transformWithOptions(geometry, false, options); + var feature = new ol.Feature(geometry); + feature.setId(id); + feature.setProperties(values.tags); + state.features.push(feature); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.OSMXML.readNd_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'nd', 'localName should be nd'); + var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); + values.ndrefs.push(node.getAttribute('ref')); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.OSMXML.readTag_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'tag', 'localName should be tag'); + var values = /** @type {Object} */ (objectStack[objectStack.length - 1]); + values.tags[node.getAttribute('k')] = node.getAttribute('v'); +}; + + +/** + * @const + * @private + * @type {Array.<string>} + */ +ol.format.OSMXML.NAMESPACE_URIS_ = [ + null +]; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OSMXML.WAY_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OSMXML.NAMESPACE_URIS_, { + 'nd': ol.format.OSMXML.readNd_, + 'tag': ol.format.OSMXML.readTag_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OSMXML.PARSERS_ = ol.xml.makeStructureNS( + ol.format.OSMXML.NAMESPACE_URIS_, { + 'node': ol.format.OSMXML.readNode_, + 'way': ol.format.OSMXML.readWay_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OSMXML.NODE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OSMXML.NAMESPACE_URIS_, { + 'tag': ol.format.OSMXML.readTag_ + }); + + +/** + * Read all features from an OSM source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.OSMXML.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.OSMXML.prototype.readFeaturesFromNode = function(node, opt_options) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + var options = this.getReadOptions(node, opt_options); + if (node.localName == 'osm') { + var state = ol.xml.pushParseAndPop({ + nodes: {}, + features: [] + }, ol.format.OSMXML.PARSERS_, node, [options]); + if (state.features) { + return state.features; + } + } + return []; +}; + + +/** + * Read the projection from an OSM source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + * @api stable + */ +ol.format.OSMXML.prototype.readProjection; + +goog.provide('ol.format.XLink'); + + +/** + * @const + * @type {string} + */ +ol.format.XLink.NAMESPACE_URI = 'http://www.w3.org/1999/xlink'; + + +/** + * @param {Node} node Node. + * @return {boolean|undefined} Boolean. + */ +ol.format.XLink.readHref = function(node) { + return node.getAttributeNS(ol.format.XLink.NAMESPACE_URI, 'href'); +}; + +goog.provide('ol.format.XML'); + +goog.require('goog.asserts'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Generic format for reading non-feature XML data + * + * @constructor + * @struct + */ +ol.format.XML = function() { +}; + + +/** + * @param {Document|Node|string} source Source. + * @return {Object} The parsed result. + */ +ol.format.XML.prototype.read = function(source) { + if (ol.xml.isDocument(source)) { + return this.readFromDocument(/** @type {Document} */ (source)); + } else if (ol.xml.isNode(source)) { + return this.readFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readFromDocument(doc); + } else { + goog.asserts.fail(); + return null; + } +}; + + +/** + * @param {Document} doc Document. + * @return {Object} + */ +ol.format.XML.prototype.readFromDocument = goog.abstractMethod; + + +/** + * @param {Node} node Node. + * @return {Object} + */ +ol.format.XML.prototype.readFromNode = goog.abstractMethod; + +goog.provide('ol.format.OWS'); + +goog.require('goog.asserts'); +goog.require('ol.format.XLink'); +goog.require('ol.format.XML'); +goog.require('ol.format.XSD'); +goog.require('ol.xml'); + + +/** + * @constructor + * @extends {ol.format.XML} + */ +ol.format.OWS = function() { + ol.format.XML.call(this); +}; +ol.inherits(ol.format.OWS, ol.format.XML); + + +/** + * @param {Document} doc Document. + * @return {Object} OWS object. + */ +ol.format.OWS.prototype.readFromDocument = function(doc) { + goog.asserts.assert(doc.nodeType == Node.DOCUMENT_NODE, + 'doc.nodeType should be DOCUMENT'); + for (var n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFromNode(n); + } + } + return null; +}; + + +/** + * @param {Node} node Node. + * @return {Object} OWS object. + */ +ol.format.OWS.prototype.readFromNode = function(node) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + var owsObject = ol.xml.pushParseAndPop({}, + ol.format.OWS.PARSERS_, node, []); + return owsObject ? owsObject : null; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The address. + */ +ol.format.OWS.readAddress_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Address', + 'localName should be Address'); + return ol.xml.pushParseAndPop({}, + ol.format.OWS.ADDRESS_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The values. + */ +ol.format.OWS.readAllowedValues_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'AllowedValues', + 'localName should be AllowedValues'); + return ol.xml.pushParseAndPop({}, + ol.format.OWS.ALLOWED_VALUES_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The constraint. + */ +ol.format.OWS.readConstraint_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Constraint', + 'localName should be Constraint'); + var name = node.getAttribute('name'); + if (!name) { + return undefined; + } + return ol.xml.pushParseAndPop({'name': name}, + ol.format.OWS.CONSTRAINT_PARSERS_, node, + objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The contact info. + */ +ol.format.OWS.readContactInfo_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'ContactInfo', + 'localName should be ContactInfo'); + return ol.xml.pushParseAndPop({}, + ol.format.OWS.CONTACT_INFO_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The DCP. + */ +ol.format.OWS.readDcp_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'DCP', 'localName should be DCP'); + return ol.xml.pushParseAndPop({}, + ol.format.OWS.DCP_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The GET object. + */ +ol.format.OWS.readGet_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Get', 'localName should be Get'); + var href = ol.format.XLink.readHref(node); + if (!href) { + return undefined; + } + return ol.xml.pushParseAndPop({'href': href}, + ol.format.OWS.REQUEST_METHOD_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The HTTP object. + */ +ol.format.OWS.readHttp_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'HTTP', 'localName should be HTTP'); + return ol.xml.pushParseAndPop({}, ol.format.OWS.HTTP_PARSERS_, + node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The operation. + */ +ol.format.OWS.readOperation_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Operation', + 'localName should be Operation'); + var name = node.getAttribute('name'); + var value = ol.xml.pushParseAndPop({}, + ol.format.OWS.OPERATION_PARSERS_, node, objectStack); + if (!value) { + return undefined; + } + var object = /** @type {Object} */ + (objectStack[objectStack.length - 1]); + goog.asserts.assert(goog.isObject(object), 'object should be an Object'); + object[name] = value; + +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The operations metadata. + */ +ol.format.OWS.readOperationsMetadata_ = function(node, + objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'OperationsMetadata', + 'localName should be OperationsMetadata'); + return ol.xml.pushParseAndPop({}, + ol.format.OWS.OPERATIONS_METADATA_PARSERS_, node, + objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The phone. + */ +ol.format.OWS.readPhone_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Phone', 'localName should be Phone'); + return ol.xml.pushParseAndPop({}, + ol.format.OWS.PHONE_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The service identification. + */ +ol.format.OWS.readServiceIdentification_ = function(node, + objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'ServiceIdentification', + 'localName should be ServiceIdentification'); + return ol.xml.pushParseAndPop( + {}, ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_, node, + objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The service contact. + */ +ol.format.OWS.readServiceContact_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'ServiceContact', + 'localName should be ServiceContact'); + return ol.xml.pushParseAndPop( + {}, ol.format.OWS.SERVICE_CONTACT_PARSERS_, node, + objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} The service provider. + */ +ol.format.OWS.readServiceProvider_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'ServiceProvider', + 'localName should be ServiceProvider'); + return ol.xml.pushParseAndPop( + {}, ol.format.OWS.SERVICE_PROVIDER_PARSERS_, node, + objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {string|undefined} The value. + */ +ol.format.OWS.readValue_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Value', 'localName should be Value'); + return ol.format.XSD.readString(node); +}; + + +/** + * @const + * @type {Array.<string>} + * @private + */ +ol.format.OWS.NAMESPACE_URIS_ = [ + null, + 'http://www.opengis.net/ows/1.1' +]; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'ServiceIdentification': ol.xml.makeObjectPropertySetter( + ol.format.OWS.readServiceIdentification_), + 'ServiceProvider': ol.xml.makeObjectPropertySetter( + ol.format.OWS.readServiceProvider_), + 'OperationsMetadata': ol.xml.makeObjectPropertySetter( + ol.format.OWS.readOperationsMetadata_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.ADDRESS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'DeliveryPoint': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'AdministrativeArea': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'PostalCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'ElectronicMailAddress': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.ALLOWED_VALUES_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'Value': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readValue_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.CONSTRAINT_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'AllowedValues': ol.xml.makeObjectPropertySetter( + ol.format.OWS.readAllowedValues_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.CONTACT_INFO_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'Phone': ol.xml.makeObjectPropertySetter(ol.format.OWS.readPhone_), + 'Address': ol.xml.makeObjectPropertySetter(ol.format.OWS.readAddress_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.DCP_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'HTTP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readHttp_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.HTTP_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'Get': ol.xml.makeObjectPropertyPusher(ol.format.OWS.readGet_), + 'Post': undefined // TODO + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.OPERATION_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'DCP': ol.xml.makeObjectPropertySetter(ol.format.OWS.readDcp_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.OPERATIONS_METADATA_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'Operation': ol.format.OWS.readOperation_ + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.PHONE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'Voice': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Facsimile': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.REQUEST_METHOD_PARSERS_ = ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'Constraint': ol.xml.makeObjectPropertyPusher( + ol.format.OWS.readConstraint_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.SERVICE_CONTACT_PARSERS_ = + ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'IndividualName': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'PositionName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'ContactInfo': ol.xml.makeObjectPropertySetter( + ol.format.OWS.readContactInfo_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.SERVICE_IDENTIFICATION_PARSERS_ = + ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'ServiceTypeVersion': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'ServiceType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.OWS.SERVICE_PROVIDER_PARSERS_ = + ol.xml.makeStructureNS( + ol.format.OWS.NAMESPACE_URIS_, { + 'ProviderName': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'ProviderSite': ol.xml.makeObjectPropertySetter(ol.format.XLink.readHref), + 'ServiceContact': ol.xml.makeObjectPropertySetter( + ol.format.OWS.readServiceContact_) + }); + +goog.provide('ol.geom.flat.flip'); + +goog.require('goog.asserts'); + + +/** + * @param {Array.<number>} flatCoordinates Flat coordinates. + * @param {number} offset Offset. + * @param {number} end End. + * @param {number} stride Stride. + * @param {Array.<number>=} opt_dest Destination. + * @param {number=} opt_destOffset Destination offset. + * @return {Array.<number>} Flat coordinates. + */ +ol.geom.flat.flip.flipXY = function(flatCoordinates, offset, end, stride, opt_dest, opt_destOffset) { + var dest, destOffset; + if (opt_dest !== undefined) { + dest = opt_dest; + destOffset = opt_destOffset !== undefined ? opt_destOffset : 0; + } else { + goog.asserts.assert(opt_destOffset === undefined, + 'opt_destOffSet should be defined'); + dest = []; + destOffset = 0; + } + var j = offset; + while (j < end) { + var x = flatCoordinates[j++]; + dest[destOffset++] = flatCoordinates[j++]; + dest[destOffset++] = x; + for (var k = 2; k < stride; ++k) { + dest[destOffset++] = flatCoordinates[j++]; + } + } + dest.length = destOffset; + return dest; +}; + +goog.provide('ol.format.Polyline'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.format.Feature'); +goog.require('ol.format.TextFeature'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.flip'); +goog.require('ol.geom.flat.inflate'); +goog.require('ol.proj'); + + +/** + * @classdesc + * Feature format for reading and writing data in the Encoded + * Polyline Algorithm Format. + * + * @constructor + * @extends {ol.format.TextFeature} + * @param {olx.format.PolylineOptions=} opt_options + * Optional configuration object. + * @api stable + */ +ol.format.Polyline = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.TextFeature.call(this); + + /** + * @inheritDoc + */ + this.defaultDataProjection = ol.proj.get('EPSG:4326'); + + /** + * @private + * @type {number} + */ + this.factor_ = options.factor ? options.factor : 1e5; + + /** + * @private + * @type {ol.geom.GeometryLayout} + */ + this.geometryLayout_ = options.geometryLayout ? + options.geometryLayout : ol.geom.GeometryLayout.XY; +}; +ol.inherits(ol.format.Polyline, ol.format.TextFeature); + + +/** + * Encode a list of n-dimensional points and return an encoded string + * + * Attention: This function will modify the passed array! + * + * @param {Array.<number>} numbers A list of n-dimensional points. + * @param {number} stride The number of dimension of the points in the list. + * @param {number=} opt_factor The factor by which the numbers will be + * multiplied. The remaining decimal places will get rounded away. + * Default is `1e5`. + * @return {string} The encoded string. + * @api + */ +ol.format.Polyline.encodeDeltas = function(numbers, stride, opt_factor) { + var factor = opt_factor ? opt_factor : 1e5; + var d; + + var lastNumbers = new Array(stride); + for (d = 0; d < stride; ++d) { + lastNumbers[d] = 0; + } + + var i, ii; + for (i = 0, ii = numbers.length; i < ii;) { + for (d = 0; d < stride; ++d, ++i) { + var num = numbers[i]; + var delta = num - lastNumbers[d]; + lastNumbers[d] = num; + + numbers[i] = delta; + } + } + + return ol.format.Polyline.encodeFloats(numbers, factor); +}; + + +/** + * Decode a list of n-dimensional points from an encoded string + * + * @param {string} encoded An encoded string. + * @param {number} stride The number of dimension of the points in the + * encoded string. + * @param {number=} opt_factor The factor by which the resulting numbers will + * be divided. Default is `1e5`. + * @return {Array.<number>} A list of n-dimensional points. + * @api + */ +ol.format.Polyline.decodeDeltas = function(encoded, stride, opt_factor) { + var factor = opt_factor ? opt_factor : 1e5; + var d; + + /** @type {Array.<number>} */ + var lastNumbers = new Array(stride); + for (d = 0; d < stride; ++d) { + lastNumbers[d] = 0; + } + + var numbers = ol.format.Polyline.decodeFloats(encoded, factor); + + var i, ii; + for (i = 0, ii = numbers.length; i < ii;) { + for (d = 0; d < stride; ++d, ++i) { + lastNumbers[d] += numbers[i]; + + numbers[i] = lastNumbers[d]; + } + } + + return numbers; +}; + + +/** + * Encode a list of floating point numbers and return an encoded string + * + * Attention: This function will modify the passed array! + * + * @param {Array.<number>} numbers A list of floating point numbers. + * @param {number=} opt_factor The factor by which the numbers will be + * multiplied. The remaining decimal places will get rounded away. + * Default is `1e5`. + * @return {string} The encoded string. + * @api + */ +ol.format.Polyline.encodeFloats = function(numbers, opt_factor) { + var factor = opt_factor ? opt_factor : 1e5; + var i, ii; + for (i = 0, ii = numbers.length; i < ii; ++i) { + numbers[i] = Math.round(numbers[i] * factor); + } + + return ol.format.Polyline.encodeSignedIntegers(numbers); +}; + + +/** + * Decode a list of floating point numbers from an encoded string + * + * @param {string} encoded An encoded string. + * @param {number=} opt_factor The factor by which the result will be divided. + * Default is `1e5`. + * @return {Array.<number>} A list of floating point numbers. + * @api + */ +ol.format.Polyline.decodeFloats = function(encoded, opt_factor) { + var factor = opt_factor ? opt_factor : 1e5; + var numbers = ol.format.Polyline.decodeSignedIntegers(encoded); + var i, ii; + for (i = 0, ii = numbers.length; i < ii; ++i) { + numbers[i] /= factor; + } + return numbers; +}; + + +/** + * Encode a list of signed integers and return an encoded string + * + * Attention: This function will modify the passed array! + * + * @param {Array.<number>} numbers A list of signed integers. + * @return {string} The encoded string. + */ +ol.format.Polyline.encodeSignedIntegers = function(numbers) { + var i, ii; + for (i = 0, ii = numbers.length; i < ii; ++i) { + var num = numbers[i]; + numbers[i] = (num < 0) ? ~(num << 1) : (num << 1); + } + return ol.format.Polyline.encodeUnsignedIntegers(numbers); +}; + + +/** + * Decode a list of signed integers from an encoded string + * + * @param {string} encoded An encoded string. + * @return {Array.<number>} A list of signed integers. + */ +ol.format.Polyline.decodeSignedIntegers = function(encoded) { + var numbers = ol.format.Polyline.decodeUnsignedIntegers(encoded); + var i, ii; + for (i = 0, ii = numbers.length; i < ii; ++i) { + var num = numbers[i]; + numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); + } + return numbers; +}; + + +/** + * Encode a list of unsigned integers and return an encoded string + * + * @param {Array.<number>} numbers A list of unsigned integers. + * @return {string} The encoded string. + */ +ol.format.Polyline.encodeUnsignedIntegers = function(numbers) { + var encoded = ''; + var i, ii; + for (i = 0, ii = numbers.length; i < ii; ++i) { + encoded += ol.format.Polyline.encodeUnsignedInteger(numbers[i]); + } + return encoded; +}; + + +/** + * Decode a list of unsigned integers from an encoded string + * + * @param {string} encoded An encoded string. + * @return {Array.<number>} A list of unsigned integers. + */ +ol.format.Polyline.decodeUnsignedIntegers = function(encoded) { + var numbers = []; + var current = 0; + var shift = 0; + var i, ii; + for (i = 0, ii = encoded.length; i < ii; ++i) { + var b = encoded.charCodeAt(i) - 63; + current |= (b & 0x1f) << shift; + if (b < 0x20) { + numbers.push(current); + current = 0; + shift = 0; + } else { + shift += 5; + } + } + return numbers; +}; + + +/** + * Encode one single unsigned integer and return an encoded string + * + * @param {number} num Unsigned integer that should be encoded. + * @return {string} The encoded string. + */ +ol.format.Polyline.encodeUnsignedInteger = function(num) { + var value, encoded = ''; + while (num >= 0x20) { + value = (0x20 | (num & 0x1f)) + 63; + encoded += String.fromCharCode(value); + num >>= 5; + } + value = num + 63; + encoded += String.fromCharCode(value); + return encoded; +}; + + +/** + * Read the feature from the Polyline source. The coordinates are assumed to be + * in two dimensions and in latitude, longitude order. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @api stable + */ +ol.format.Polyline.prototype.readFeature; + + +/** + * @inheritDoc + */ +ol.format.Polyline.prototype.readFeatureFromText = function(text, opt_options) { + var geometry = this.readGeometryFromText(text, opt_options); + return new ol.Feature(geometry); +}; + + +/** + * Read the feature from the source. As Polyline sources contain a single + * feature, this will return the feature in an array. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.Polyline.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.Polyline.prototype.readFeaturesFromText = function(text, opt_options) { + var feature = this.readFeatureFromText(text, opt_options); + return [feature]; +}; + + +/** + * Read the geometry from the source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.geom.Geometry} Geometry. + * @api stable + */ +ol.format.Polyline.prototype.readGeometry; + + +/** + * @inheritDoc + */ +ol.format.Polyline.prototype.readGeometryFromText = function(text, opt_options) { + var stride = ol.geom.SimpleGeometry.getStrideForLayout(this.geometryLayout_); + var flatCoordinates = ol.format.Polyline.decodeDeltas( + text, stride, this.factor_); + ol.geom.flat.flip.flipXY( + flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); + var coordinates = ol.geom.flat.inflate.coordinates( + flatCoordinates, 0, flatCoordinates.length, stride); + + return /** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions( + new ol.geom.LineString(coordinates, this.geometryLayout_), false, + this.adaptOptions(opt_options))); +}; + + +/** + * Read the projection from a Polyline source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {ol.proj.Projection} Projection. + * @api stable + */ +ol.format.Polyline.prototype.readProjection; + + +/** + * @inheritDoc + */ +ol.format.Polyline.prototype.writeFeatureText = function(feature, opt_options) { + var geometry = feature.getGeometry(); + if (geometry) { + return this.writeGeometryText(geometry, opt_options); + } else { + goog.asserts.fail('geometry needs to be defined'); + return ''; + } +}; + + +/** + * @inheritDoc + */ +ol.format.Polyline.prototype.writeFeaturesText = function(features, opt_options) { + goog.asserts.assert(features.length == 1, + 'features array should have 1 item'); + return this.writeFeatureText(features[0], opt_options); +}; + + +/** + * Write a single geometry in Polyline format. + * + * @function + * @param {ol.geom.Geometry} geometry Geometry. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} Geometry. + * @api stable + */ +ol.format.Polyline.prototype.writeGeometry; + + +/** + * @inheritDoc + */ +ol.format.Polyline.prototype.writeGeometryText = function(geometry, opt_options) { + goog.asserts.assertInstanceof(geometry, ol.geom.LineString, + 'geometry should be an ol.geom.LineString'); + geometry = /** @type {ol.geom.LineString} */ + (ol.format.Feature.transformWithOptions( + geometry, true, this.adaptOptions(opt_options))); + var flatCoordinates = geometry.getFlatCoordinates(); + var stride = geometry.getStride(); + ol.geom.flat.flip.flipXY( + flatCoordinates, 0, flatCoordinates.length, stride, flatCoordinates); + return ol.format.Polyline.encodeDeltas(flatCoordinates, stride, this.factor_); +}; + +goog.provide('ol.format.TopoJSON'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.format.Feature'); +goog.require('ol.format.JSONFeature'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.object'); +goog.require('ol.proj'); + + +/** + * @classdesc + * Feature format for reading data in the TopoJSON format. + * + * @constructor + * @extends {ol.format.JSONFeature} + * @param {olx.format.TopoJSONOptions=} opt_options Options. + * @api stable + */ +ol.format.TopoJSON = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.JSONFeature.call(this); + + /** + * @inheritDoc + */ + this.defaultDataProjection = ol.proj.get( + options.defaultDataProjection ? + options.defaultDataProjection : 'EPSG:4326'); + +}; +ol.inherits(ol.format.TopoJSON, ol.format.JSONFeature); + + +/** + * @const {Array.<string>} + * @private + */ +ol.format.TopoJSON.EXTENSIONS_ = ['.topojson']; + + +/** + * Concatenate arcs into a coordinate array. + * @param {Array.<number>} indices Indices of arcs to concatenate. Negative + * values indicate arcs need to be reversed. + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs (already + * transformed). + * @return {Array.<ol.Coordinate>} Coordinates array. + * @private + */ +ol.format.TopoJSON.concatenateArcs_ = function(indices, arcs) { + /** @type {Array.<ol.Coordinate>} */ + var coordinates = []; + var index, arc; + var i, ii; + var j, jj; + for (i = 0, ii = indices.length; i < ii; ++i) { + index = indices[i]; + if (i > 0) { + // splicing together arcs, discard last point + coordinates.pop(); + } + if (index >= 0) { + // forward arc + arc = arcs[index]; + } else { + // reverse arc + arc = arcs[~index].slice().reverse(); + } + coordinates.push.apply(coordinates, arc); + } + // provide fresh copies of coordinate arrays + for (j = 0, jj = coordinates.length; j < jj; ++j) { + coordinates[j] = coordinates[j].slice(); + } + return coordinates; +}; + + +/** + * Create a point from a TopoJSON geometry object. + * + * @param {TopoJSONGeometry} object TopoJSON object. + * @param {Array.<number>} scale Scale for each dimension. + * @param {Array.<number>} translate Translation for each dimension. + * @return {ol.geom.Point} Geometry. + * @private + */ +ol.format.TopoJSON.readPointGeometry_ = function(object, scale, translate) { + var coordinates = object.coordinates; + if (scale && translate) { + ol.format.TopoJSON.transformVertex_(coordinates, scale, translate); + } + return new ol.geom.Point(coordinates); +}; + + +/** + * Create a multi-point from a TopoJSON geometry object. + * + * @param {TopoJSONGeometry} object TopoJSON object. + * @param {Array.<number>} scale Scale for each dimension. + * @param {Array.<number>} translate Translation for each dimension. + * @return {ol.geom.MultiPoint} Geometry. + * @private + */ +ol.format.TopoJSON.readMultiPointGeometry_ = function(object, scale, + translate) { + var coordinates = object.coordinates; + var i, ii; + if (scale && translate) { + for (i = 0, ii = coordinates.length; i < ii; ++i) { + ol.format.TopoJSON.transformVertex_(coordinates[i], scale, translate); + } + } + return new ol.geom.MultiPoint(coordinates); +}; + + +/** + * Create a linestring from a TopoJSON geometry object. + * + * @param {TopoJSONGeometry} object TopoJSON object. + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. + * @return {ol.geom.LineString} Geometry. + * @private + */ +ol.format.TopoJSON.readLineStringGeometry_ = function(object, arcs) { + var coordinates = ol.format.TopoJSON.concatenateArcs_(object.arcs, arcs); + return new ol.geom.LineString(coordinates); +}; + + +/** + * Create a multi-linestring from a TopoJSON geometry object. + * + * @param {TopoJSONGeometry} object TopoJSON object. + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. + * @return {ol.geom.MultiLineString} Geometry. + * @private + */ +ol.format.TopoJSON.readMultiLineStringGeometry_ = function(object, arcs) { + var coordinates = []; + var i, ii; + for (i = 0, ii = object.arcs.length; i < ii; ++i) { + coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs); + } + return new ol.geom.MultiLineString(coordinates); +}; + + +/** + * Create a polygon from a TopoJSON geometry object. + * + * @param {TopoJSONGeometry} object TopoJSON object. + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. + * @return {ol.geom.Polygon} Geometry. + * @private + */ +ol.format.TopoJSON.readPolygonGeometry_ = function(object, arcs) { + var coordinates = []; + var i, ii; + for (i = 0, ii = object.arcs.length; i < ii; ++i) { + coordinates[i] = ol.format.TopoJSON.concatenateArcs_(object.arcs[i], arcs); + } + return new ol.geom.Polygon(coordinates); +}; + + +/** + * Create a multi-polygon from a TopoJSON geometry object. + * + * @param {TopoJSONGeometry} object TopoJSON object. + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. + * @return {ol.geom.MultiPolygon} Geometry. + * @private + */ +ol.format.TopoJSON.readMultiPolygonGeometry_ = function(object, arcs) { + var coordinates = []; + var polyArray, ringCoords, j, jj; + var i, ii; + for (i = 0, ii = object.arcs.length; i < ii; ++i) { + // for each polygon + polyArray = object.arcs[i]; + ringCoords = []; + for (j = 0, jj = polyArray.length; j < jj; ++j) { + // for each ring + ringCoords[j] = ol.format.TopoJSON.concatenateArcs_(polyArray[j], arcs); + } + coordinates[i] = ringCoords; + } + return new ol.geom.MultiPolygon(coordinates); +}; + + +/** + * @inheritDoc + */ +ol.format.TopoJSON.prototype.getExtensions = function() { + return ol.format.TopoJSON.EXTENSIONS_; +}; + + +/** + * Create features from a TopoJSON GeometryCollection object. + * + * @param {TopoJSONGeometryCollection} collection TopoJSON Geometry + * object. + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. + * @param {Array.<number>} scale Scale for each dimension. + * @param {Array.<number>} translate Translation for each dimension. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Array of features. + * @private + */ +ol.format.TopoJSON.readFeaturesFromGeometryCollection_ = function( + collection, arcs, scale, translate, opt_options) { + var geometries = collection.geometries; + var features = []; + var i, ii; + for (i = 0, ii = geometries.length; i < ii; ++i) { + features[i] = ol.format.TopoJSON.readFeatureFromGeometry_( + geometries[i], arcs, scale, translate, opt_options); + } + return features; +}; + + +/** + * Create a feature from a TopoJSON geometry object. + * + * @param {TopoJSONGeometry} object TopoJSON geometry object. + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. + * @param {Array.<number>} scale Scale for each dimension. + * @param {Array.<number>} translate Translation for each dimension. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @private + */ +ol.format.TopoJSON.readFeatureFromGeometry_ = function(object, arcs, + scale, translate, opt_options) { + var geometry; + var type = object.type; + var geometryReader = ol.format.TopoJSON.GEOMETRY_READERS_[type]; + goog.asserts.assert(geometryReader, 'geometryReader should be defined'); + if ((type === 'Point') || (type === 'MultiPoint')) { + geometry = geometryReader(object, scale, translate); + } else { + geometry = geometryReader(object, arcs); + } + var feature = new ol.Feature(); + feature.setGeometry(/** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions(geometry, false, opt_options))); + if (object.id !== undefined) { + feature.setId(object.id); + } + if (object.properties) { + feature.setProperties(object.properties); + } + return feature; +}; + + +/** + * Read all features from a TopoJSON source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.TopoJSON.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.TopoJSON.prototype.readFeaturesFromObject = function( + object, opt_options) { + if (object.type == 'Topology') { + var topoJSONTopology = /** @type {TopoJSONTopology} */ (object); + var transform, scale = null, translate = null; + if (topoJSONTopology.transform) { + transform = topoJSONTopology.transform; + scale = transform.scale; + translate = transform.translate; + } + var arcs = topoJSONTopology.arcs; + if (transform) { + ol.format.TopoJSON.transformArcs_(arcs, scale, translate); + } + /** @type {Array.<ol.Feature>} */ + var features = []; + var topoJSONFeatures = ol.object.getValues(topoJSONTopology.objects); + var i, ii; + var feature; + for (i = 0, ii = topoJSONFeatures.length; i < ii; ++i) { + if (topoJSONFeatures[i].type === 'GeometryCollection') { + feature = /** @type {TopoJSONGeometryCollection} */ + (topoJSONFeatures[i]); + features.push.apply(features, + ol.format.TopoJSON.readFeaturesFromGeometryCollection_( + feature, arcs, scale, translate, opt_options)); + } else { + feature = /** @type {TopoJSONGeometry} */ + (topoJSONFeatures[i]); + features.push(ol.format.TopoJSON.readFeatureFromGeometry_( + feature, arcs, scale, translate, opt_options)); + } + } + return features; + } else { + return []; + } +}; + + +/** + * Apply a linear transform to array of arcs. The provided array of arcs is + * modified in place. + * + * @param {Array.<Array.<ol.Coordinate>>} arcs Array of arcs. + * @param {Array.<number>} scale Scale for each dimension. + * @param {Array.<number>} translate Translation for each dimension. + * @private + */ +ol.format.TopoJSON.transformArcs_ = function(arcs, scale, translate) { + var i, ii; + for (i = 0, ii = arcs.length; i < ii; ++i) { + ol.format.TopoJSON.transformArc_(arcs[i], scale, translate); + } +}; + + +/** + * Apply a linear transform to an arc. The provided arc is modified in place. + * + * @param {Array.<ol.Coordinate>} arc Arc. + * @param {Array.<number>} scale Scale for each dimension. + * @param {Array.<number>} translate Translation for each dimension. + * @private + */ +ol.format.TopoJSON.transformArc_ = function(arc, scale, translate) { + var x = 0; + var y = 0; + var vertex; + var i, ii; + for (i = 0, ii = arc.length; i < ii; ++i) { + vertex = arc[i]; + x += vertex[0]; + y += vertex[1]; + vertex[0] = x; + vertex[1] = y; + ol.format.TopoJSON.transformVertex_(vertex, scale, translate); + } +}; + + +/** + * Apply a linear transform to a vertex. The provided vertex is modified in + * place. + * + * @param {ol.Coordinate} vertex Vertex. + * @param {Array.<number>} scale Scale for each dimension. + * @param {Array.<number>} translate Translation for each dimension. + * @private + */ +ol.format.TopoJSON.transformVertex_ = function(vertex, scale, translate) { + vertex[0] = vertex[0] * scale[0] + translate[0]; + vertex[1] = vertex[1] * scale[1] + translate[1]; +}; + + +/** + * Read the projection from a TopoJSON source. + * + * @function + * @param {Document|Node|Object|string} object Source. + * @return {ol.proj.Projection} Projection. + * @api stable + */ +ol.format.TopoJSON.prototype.readProjection = function(object) { + return this.defaultDataProjection; +}; + + +/** + * @const + * @private + * @type {Object.<string, function(TopoJSONGeometry, Array, ...Array): ol.geom.Geometry>} + */ +ol.format.TopoJSON.GEOMETRY_READERS_ = { + 'Point': ol.format.TopoJSON.readPointGeometry_, + 'LineString': ol.format.TopoJSON.readLineStringGeometry_, + 'Polygon': ol.format.TopoJSON.readPolygonGeometry_, + 'MultiPoint': ol.format.TopoJSON.readMultiPointGeometry_, + 'MultiLineString': ol.format.TopoJSON.readMultiLineStringGeometry_, + 'MultiPolygon': ol.format.TopoJSON.readMultiPolygonGeometry_ +}; + +goog.provide('ol.format.WFS'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.format.GML3'); +goog.require('ol.format.GMLBase'); +goog.require('ol.format.ogc.filter'); +goog.require('ol.format.ogc.filter.Bbox'); +goog.require('ol.format.ogc.filter.ComparisonBinary'); +goog.require('ol.format.ogc.filter.LogicalBinary'); +goog.require('ol.format.ogc.filter.Not'); +goog.require('ol.format.ogc.filter.IsBetween'); +goog.require('ol.format.ogc.filter.IsNull'); +goog.require('ol.format.ogc.filter.IsLike'); +goog.require('ol.format.XMLFeature'); +goog.require('ol.format.XSD'); +goog.require('ol.geom.Geometry'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Feature format for reading and writing data in the WFS format. + * By default, supports WFS version 1.1.0. You can pass a GML format + * as option if you want to read a WFS that contains GML2 (WFS 1.0.0). + * Also see {@link ol.format.GMLBase} which is used by this format. + * + * @constructor + * @param {olx.format.WFSOptions=} opt_options + * Optional configuration object. + * @extends {ol.format.XMLFeature} + * @api stable + */ +ol.format.WFS = function(opt_options) { + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {Array.<string>|string|undefined} + */ + this.featureType_ = options.featureType; + + /** + * @private + * @type {Object.<string, string>|string|undefined} + */ + this.featureNS_ = options.featureNS; + + /** + * @private + * @type {ol.format.GMLBase} + */ + this.gmlFormat_ = options.gmlFormat ? + options.gmlFormat : new ol.format.GML3(); + + /** + * @private + * @type {string} + */ + this.schemaLocation_ = options.schemaLocation ? + options.schemaLocation : ol.format.WFS.SCHEMA_LOCATION; + + ol.format.XMLFeature.call(this); +}; +ol.inherits(ol.format.WFS, ol.format.XMLFeature); + + +/** + * @const + * @type {string} + */ +ol.format.WFS.FEATURE_PREFIX = 'feature'; + + +/** + * @const + * @type {string} + */ +ol.format.WFS.XMLNS = 'http://www.w3.org/2000/xmlns/'; + + +/** + * @const + * @type {string} + */ +ol.format.WFS.SCHEMA_LOCATION = 'http://www.opengis.net/wfs ' + + 'http://schemas.opengis.net/wfs/1.1.0/wfs.xsd'; + + +/** + * Read all features from a WFS FeatureCollection. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.WFS.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.WFS.prototype.readFeaturesFromNode = function(node, opt_options) { + var context = /** @type {ol.XmlNodeStackItem} */ ({ + 'featureType': this.featureType_, + 'featureNS': this.featureNS_ + }); + ol.object.assign(context, this.getReadOptions(node, + opt_options ? opt_options : {})); + var objectStack = [context]; + this.gmlFormat_.FEATURE_COLLECTION_PARSERS[ol.format.GMLBase.GMLNS][ + 'featureMember'] = + ol.xml.makeArrayPusher(ol.format.GMLBase.prototype.readFeaturesInternal); + var features = ol.xml.pushParseAndPop([], + this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, + objectStack, this.gmlFormat_); + if (!features) { + features = []; + } + return features; +}; + + +/** + * Read transaction response of the source. + * + * @param {Document|Node|Object|string} source Source. + * @return {ol.WFSTransactionResponse|undefined} Transaction response. + * @api stable + */ +ol.format.WFS.prototype.readTransactionResponse = function(source) { + if (ol.xml.isDocument(source)) { + return this.readTransactionResponseFromDocument( + /** @type {Document} */ (source)); + } else if (ol.xml.isNode(source)) { + return this.readTransactionResponseFromNode(/** @type {Node} */ (source)); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readTransactionResponseFromDocument(doc); + } else { + goog.asserts.fail('Unknown source type'); + return undefined; + } +}; + + +/** + * Read feature collection metadata of the source. + * + * @param {Document|Node|Object|string} source Source. + * @return {ol.WFSFeatureCollectionMetadata|undefined} + * FeatureCollection metadata. + * @api stable + */ +ol.format.WFS.prototype.readFeatureCollectionMetadata = function(source) { + if (ol.xml.isDocument(source)) { + return this.readFeatureCollectionMetadataFromDocument( + /** @type {Document} */ (source)); + } else if (ol.xml.isNode(source)) { + return this.readFeatureCollectionMetadataFromNode( + /** @type {Node} */ (source)); + } else if (typeof source === 'string') { + var doc = ol.xml.parse(source); + return this.readFeatureCollectionMetadataFromDocument(doc); + } else { + goog.asserts.fail('Unknown source type'); + return undefined; + } +}; + + +/** + * @param {Document} doc Document. + * @return {ol.WFSFeatureCollectionMetadata|undefined} + * FeatureCollection metadata. + */ +ol.format.WFS.prototype.readFeatureCollectionMetadataFromDocument = function(doc) { + goog.asserts.assert(doc.nodeType == Node.DOCUMENT_NODE, + 'doc.nodeType should be DOCUMENT'); + for (var n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFeatureCollectionMetadataFromNode(n); + } + } + return undefined; +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WFS.FEATURE_COLLECTION_PARSERS_ = { + 'http://www.opengis.net/gml': { + 'boundedBy': ol.xml.makeObjectPropertySetter( + ol.format.GMLBase.prototype.readGeometryElement, 'bounds') + } +}; + + +/** + * @param {Node} node Node. + * @return {ol.WFSFeatureCollectionMetadata|undefined} + * FeatureCollection metadata. + */ +ol.format.WFS.prototype.readFeatureCollectionMetadataFromNode = function(node) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'FeatureCollection', + 'localName should be FeatureCollection'); + var result = {}; + var value = ol.format.XSD.readNonNegativeIntegerString( + node.getAttribute('numberOfFeatures')); + result['numberOfFeatures'] = value; + return ol.xml.pushParseAndPop( + /** @type {ol.WFSFeatureCollectionMetadata} */ (result), + ol.format.WFS.FEATURE_COLLECTION_PARSERS_, node, [], this.gmlFormat_); +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_ = { + 'http://www.opengis.net/wfs': { + 'totalInserted': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'totalUpdated': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'totalDeleted': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger) + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Transaction Summary. + * @private + */ +ol.format.WFS.readTransactionSummary_ = function(node, objectStack) { + return ol.xml.pushParseAndPop( + {}, ol.format.WFS.TRANSACTION_SUMMARY_PARSERS_, node, objectStack); +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WFS.OGC_FID_PARSERS_ = { + 'http://www.opengis.net/ogc': { + 'FeatureId': ol.xml.makeArrayPusher(function(node, objectStack) { + return node.getAttribute('fid'); + }) + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + */ +ol.format.WFS.fidParser_ = function(node, objectStack) { + ol.xml.parseNode(ol.format.WFS.OGC_FID_PARSERS_, node, objectStack); +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WFS.INSERT_RESULTS_PARSERS_ = { + 'http://www.opengis.net/wfs': { + 'Feature': ol.format.WFS.fidParser_ + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Array.<string>|undefined} Insert results. + * @private + */ +ol.format.WFS.readInsertResults_ = function(node, objectStack) { + return ol.xml.pushParseAndPop( + [], ol.format.WFS.INSERT_RESULTS_PARSERS_, node, objectStack); +}; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_ = { + 'http://www.opengis.net/wfs': { + 'TransactionSummary': ol.xml.makeObjectPropertySetter( + ol.format.WFS.readTransactionSummary_, 'transactionSummary'), + 'InsertResults': ol.xml.makeObjectPropertySetter( + ol.format.WFS.readInsertResults_, 'insertIds') + } +}; + + +/** + * @param {Document} doc Document. + * @return {ol.WFSTransactionResponse|undefined} Transaction response. + */ +ol.format.WFS.prototype.readTransactionResponseFromDocument = function(doc) { + goog.asserts.assert(doc.nodeType == Node.DOCUMENT_NODE, + 'doc.nodeType should be DOCUMENT'); + for (var n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readTransactionResponseFromNode(n); + } + } + return undefined; +}; + + +/** + * @param {Node} node Node. + * @return {ol.WFSTransactionResponse|undefined} Transaction response. + */ +ol.format.WFS.prototype.readTransactionResponseFromNode = function(node) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'TransactionResponse', + 'localName should be TransactionResponse'); + return ol.xml.pushParseAndPop( + /** @type {ol.WFSTransactionResponse} */({}), + ol.format.WFS.TRANSACTION_RESPONSE_PARSERS_, node, []); +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.WFS.QUERY_SERIALIZERS_ = { + 'http://www.opengis.net/wfs': { + 'PropertyName': ol.xml.makeChildAppender(ol.format.XSD.writeStringTextNode) + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeFeature_ = function(node, feature, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var featureType = context['featureType']; + var featureNS = context['featureNS']; + var child = ol.xml.createElementNS(featureNS, featureType); + node.appendChild(child); + ol.format.GML3.prototype.writeFeatureElement(child, feature, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {number|string} fid Feature identifier. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeOgcFidFilter_ = function(node, fid, objectStack) { + var filter = ol.xml.createElementNS('http://www.opengis.net/ogc', 'Filter'); + var child = ol.xml.createElementNS('http://www.opengis.net/ogc', 'FeatureId'); + filter.appendChild(child); + child.setAttribute('fid', fid); + node.appendChild(filter); +}; + + +/** + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeDelete_ = function(node, feature, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + goog.asserts.assert(feature.getId() !== undefined, 'feature should have an id'); + var featureType = context['featureType']; + var featurePrefix = context['featurePrefix']; + featurePrefix = featurePrefix ? featurePrefix : + ol.format.WFS.FEATURE_PREFIX; + var featureNS = context['featureNS']; + node.setAttribute('typeName', featurePrefix + ':' + featureType); + ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix, + featureNS); + var fid = feature.getId(); + if (fid !== undefined) { + ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack); + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.Feature} feature Feature. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeUpdate_ = function(node, feature, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + goog.asserts.assert(feature.getId() !== undefined, 'feature should have an id'); + var featureType = context['featureType']; + var featurePrefix = context['featurePrefix']; + featurePrefix = featurePrefix ? featurePrefix : + ol.format.WFS.FEATURE_PREFIX; + var featureNS = context['featureNS']; + node.setAttribute('typeName', featurePrefix + ':' + featureType); + ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix, + featureNS); + var fid = feature.getId(); + if (fid !== undefined) { + var keys = feature.getKeys(); + var values = []; + for (var i = 0, ii = keys.length; i < ii; i++) { + var value = feature.get(keys[i]); + if (value !== undefined) { + values.push({name: keys[i], value: value}); + } + } + ol.xml.pushSerializeAndPop(/** @type {ol.XmlNodeStackItem} */ ( + {node: node, 'srsName': context['srsName']}), + ol.format.WFS.TRANSACTION_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('Property'), values, + objectStack); + ol.format.WFS.writeOgcFidFilter_(node, fid, objectStack); + } +}; + + +/** + * @param {Node} node Node. + * @param {Object} pair Property name and value. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeProperty_ = function(node, pair, objectStack) { + var name = ol.xml.createElementNS('http://www.opengis.net/wfs', 'Name'); + node.appendChild(name); + ol.format.XSD.writeStringTextNode(name, pair.name); + if (pair.value !== undefined && pair.value !== null) { + var value = ol.xml.createElementNS('http://www.opengis.net/wfs', 'Value'); + node.appendChild(value); + if (pair.value instanceof ol.geom.Geometry) { + ol.format.GML3.prototype.writeGeometryElement(value, + pair.value, objectStack); + } else { + ol.format.XSD.writeStringTextNode(value, pair.value); + } + } +}; + + +/** + * @param {Node} node Node. + * @param {{vendorId: string, safeToIgnore: boolean, value: string}} + * nativeElement The native element. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeNative_ = function(node, nativeElement, objectStack) { + if (nativeElement.vendorId) { + node.setAttribute('vendorId', nativeElement.vendorId); + } + if (nativeElement.safeToIgnore !== undefined) { + node.setAttribute('safeToIgnore', nativeElement.safeToIgnore); + } + if (nativeElement.value !== undefined) { + ol.format.XSD.writeStringTextNode(node, nativeElement.value); + } +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.WFS.TRANSACTION_SERIALIZERS_ = { + 'http://www.opengis.net/wfs': { + 'Insert': ol.xml.makeChildAppender(ol.format.WFS.writeFeature_), + 'Update': ol.xml.makeChildAppender(ol.format.WFS.writeUpdate_), + 'Delete': ol.xml.makeChildAppender(ol.format.WFS.writeDelete_), + 'Property': ol.xml.makeChildAppender(ol.format.WFS.writeProperty_), + 'Native': ol.xml.makeChildAppender(ol.format.WFS.writeNative_) + } +}; + + +/** + * @param {Node} node Node. + * @param {string} featureType Feature type. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeQuery_ = function(node, featureType, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var featurePrefix = context['featurePrefix']; + var featureNS = context['featureNS']; + var propertyNames = context['propertyNames']; + var srsName = context['srsName']; + var prefix = featurePrefix ? featurePrefix + ':' : ''; + node.setAttribute('typeName', prefix + featureType); + if (srsName) { + node.setAttribute('srsName', srsName); + } + if (featureNS) { + ol.xml.setAttributeNS(node, ol.format.WFS.XMLNS, 'xmlns:' + featurePrefix, + featureNS); + } + var item = /** @type {ol.XmlNodeStackItem} */ (ol.object.assign({}, context)); + item.node = node; + ol.xml.pushSerializeAndPop(item, + ol.format.WFS.QUERY_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('PropertyName'), propertyNames, + objectStack); + var filter = context['filter']; + if (filter) { + var child = ol.xml.createElementNS('http://www.opengis.net/ogc', 'Filter'); + node.appendChild(child); + ol.format.WFS.writeFilterCondition_(child, filter, objectStack); + } +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeFilterCondition_ = function(node, filter, objectStack) { + /** @type {ol.XmlNodeStackItem} */ + var item = {node: node}; + ol.xml.pushSerializeAndPop(item, + ol.format.WFS.GETFEATURE_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory(filter.getTagName()), + [filter], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeBboxFilter_ = function(node, filter, objectStack) { + goog.asserts.assertInstanceof(filter, ol.format.ogc.filter.Bbox, + 'must be bbox filter'); + + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + context['srsName'] = filter.srsName; + + ol.format.WFS.writeOgcPropertyName_(node, filter.geometryName); + ol.format.GML3.prototype.writeGeometryElement(node, filter.extent, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeLogicalFilter_ = function(node, filter, objectStack) { + goog.asserts.assertInstanceof(filter, ol.format.ogc.filter.LogicalBinary, + 'must be logical filter'); + /** @type {ol.XmlNodeStackItem} */ + var item = {node: node}; + var conditionA = filter.conditionA; + ol.xml.pushSerializeAndPop(item, + ol.format.WFS.GETFEATURE_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory(conditionA.getTagName()), + [conditionA], objectStack); + var conditionB = filter.conditionB; + ol.xml.pushSerializeAndPop(item, + ol.format.WFS.GETFEATURE_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory(conditionB.getTagName()), + [conditionB], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeNotFilter_ = function(node, filter, objectStack) { + goog.asserts.assertInstanceof(filter, ol.format.ogc.filter.Not, + 'must be Not filter'); + /** @type {ol.XmlNodeStackItem} */ + var item = {node: node}; + var condition = filter.condition; + ol.xml.pushSerializeAndPop(item, + ol.format.WFS.GETFEATURE_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory(condition.getTagName()), + [condition], objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeComparisonFilter_ = function(node, filter, objectStack) { + goog.asserts.assertInstanceof(filter, ol.format.ogc.filter.ComparisonBinary, + 'must be binary comparison filter'); + if (filter.matchCase !== undefined) { + node.setAttribute('matchCase', filter.matchCase.toString()); + } + ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); + ol.format.WFS.writeOgcLiteral_(node, '' + filter.expression); +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeIsNullFilter_ = function(node, filter, objectStack) { + goog.asserts.assertInstanceof(filter, ol.format.ogc.filter.IsNull, + 'must be IsNull comparison filter'); + ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeIsBetweenFilter_ = function(node, filter, objectStack) { + goog.asserts.assertInstanceof(filter, ol.format.ogc.filter.IsBetween, + 'must be IsBetween comparison filter'); + ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); + ol.format.WFS.writeOgcExpression_('LowerBoundary', node, '' + filter.lowerBoundary); + ol.format.WFS.writeOgcExpression_('UpperBoundary', node, '' + filter.upperBoundary); +}; + + +/** + * @param {Node} node Node. + * @param {ol.format.ogc.filter.Filter} filter Filter. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeIsLikeFilter_ = function(node, filter, objectStack) { + goog.asserts.assertInstanceof(filter, ol.format.ogc.filter.IsLike, + 'must be IsLike comparison filter'); + node.setAttribute('wildCard', filter.wildCard); + node.setAttribute('singleChar', filter.singleChar); + node.setAttribute('escapeChar', filter.escapeChar); + if (filter.matchCase !== undefined) { + node.setAttribute('matchCase', filter.matchCase.toString()); + } + ol.format.WFS.writeOgcPropertyName_(node, filter.propertyName); + ol.format.WFS.writeOgcLiteral_(node, '' + filter.pattern); +}; + + +/** + * @param {string} tagName Tag name. + * @param {Node} node Node. + * @param {string} value Value. + * @private + */ +ol.format.WFS.writeOgcExpression_ = function(tagName, node, value) { + var property = ol.xml.createElementNS('http://www.opengis.net/ogc', tagName); + ol.format.XSD.writeStringTextNode(property, value); + node.appendChild(property); +}; + + +/** + * @param {Node} node Node. + * @param {string} value PropertyName value. + * @private + */ +ol.format.WFS.writeOgcPropertyName_ = function(node, value) { + ol.format.WFS.writeOgcExpression_('PropertyName', node, value); +}; + + +/** + * @param {Node} node Node. + * @param {string} value PropertyName value. + * @private + */ +ol.format.WFS.writeOgcLiteral_ = function(node, value) { + ol.format.WFS.writeOgcExpression_('Literal', node, value); +}; + + +/** + * @type {Object.<string, Object.<string, ol.XmlSerializer>>} + * @private + */ +ol.format.WFS.GETFEATURE_SERIALIZERS_ = { + 'http://www.opengis.net/wfs': { + 'Query': ol.xml.makeChildAppender(ol.format.WFS.writeQuery_) + }, + 'http://www.opengis.net/ogc': { + 'And': ol.xml.makeChildAppender(ol.format.WFS.writeLogicalFilter_), + 'Or': ol.xml.makeChildAppender(ol.format.WFS.writeLogicalFilter_), + 'Not': ol.xml.makeChildAppender(ol.format.WFS.writeNotFilter_), + 'BBOX': ol.xml.makeChildAppender(ol.format.WFS.writeBboxFilter_), + 'PropertyIsEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), + 'PropertyIsNotEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), + 'PropertyIsLessThan': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), + 'PropertyIsLessThanOrEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), + 'PropertyIsGreaterThan': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), + 'PropertyIsGreaterThanOrEqualTo': ol.xml.makeChildAppender(ol.format.WFS.writeComparisonFilter_), + 'PropertyIsNull': ol.xml.makeChildAppender(ol.format.WFS.writeIsNullFilter_), + 'PropertyIsBetween': ol.xml.makeChildAppender(ol.format.WFS.writeIsBetweenFilter_), + 'PropertyIsLike': ol.xml.makeChildAppender(ol.format.WFS.writeIsLikeFilter_) + } +}; + + +/** + * @param {Node} node Node. + * @param {Array.<{string}>} featureTypes Feature types. + * @param {Array.<*>} objectStack Node stack. + * @private + */ +ol.format.WFS.writeGetFeature_ = function(node, featureTypes, objectStack) { + var context = objectStack[objectStack.length - 1]; + goog.asserts.assert(goog.isObject(context), 'context should be an Object'); + var item = /** @type {ol.XmlNodeStackItem} */ (ol.object.assign({}, context)); + item.node = node; + ol.xml.pushSerializeAndPop(item, + ol.format.WFS.GETFEATURE_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('Query'), featureTypes, + objectStack); +}; + + +/** + * Encode format as WFS `GetFeature` and return the Node. + * + * @param {olx.format.WFSWriteGetFeatureOptions} options Options. + * @return {Node} Result. + * @api stable + */ +ol.format.WFS.prototype.writeGetFeature = function(options) { + var node = ol.xml.createElementNS('http://www.opengis.net/wfs', + 'GetFeature'); + node.setAttribute('service', 'WFS'); + node.setAttribute('version', '1.1.0'); + var filter; + if (options) { + if (options.handle) { + node.setAttribute('handle', options.handle); + } + if (options.outputFormat) { + node.setAttribute('outputFormat', options.outputFormat); + } + if (options.maxFeatures !== undefined) { + node.setAttribute('maxFeatures', options.maxFeatures); + } + if (options.resultType) { + node.setAttribute('resultType', options.resultType); + } + if (options.startIndex !== undefined) { + node.setAttribute('startIndex', options.startIndex); + } + if (options.count !== undefined) { + node.setAttribute('count', options.count); + } + filter = options.filter; + if (options.bbox) { + goog.asserts.assert(options.geometryName, + 'geometryName must be set when using bbox filter'); + var bbox = ol.format.ogc.filter.bbox( + options.geometryName, options.bbox, options.srsName); + if (filter) { + // if bbox and filter are both set, combine the two into a single filter + filter = ol.format.ogc.filter.and(filter, bbox); + } else { + filter = bbox; + } + } + } + ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation', this.schemaLocation_); + /** @type {ol.XmlNodeStackItem} */ + var context = { + node: node, + 'srsName': options.srsName, + 'featureNS': options.featureNS ? options.featureNS : this.featureNS_, + 'featurePrefix': options.featurePrefix, + 'geometryName': options.geometryName, + 'filter': filter, + 'propertyNames': options.propertyNames ? options.propertyNames : [] + }; + goog.asserts.assert(Array.isArray(options.featureTypes), + 'options.featureTypes should be an array'); + ol.format.WFS.writeGetFeature_(node, options.featureTypes, [context]); + return node; +}; + + +/** + * Encode format as WFS `Transaction` and return the Node. + * + * @param {Array.<ol.Feature>} inserts The features to insert. + * @param {Array.<ol.Feature>} updates The features to update. + * @param {Array.<ol.Feature>} deletes The features to delete. + * @param {olx.format.WFSWriteTransactionOptions} options Write options. + * @return {Node} Result. + * @api stable + */ +ol.format.WFS.prototype.writeTransaction = function(inserts, updates, deletes, + options) { + var objectStack = []; + var node = ol.xml.createElementNS('http://www.opengis.net/wfs', + 'Transaction'); + node.setAttribute('service', 'WFS'); + node.setAttribute('version', '1.1.0'); + var baseObj; + /** @type {ol.XmlNodeStackItem} */ + var obj; + if (options) { + baseObj = options.gmlOptions ? options.gmlOptions : {}; + if (options.handle) { + node.setAttribute('handle', options.handle); + } + } + ol.xml.setAttributeNS(node, 'http://www.w3.org/2001/XMLSchema-instance', + 'xsi:schemaLocation', this.schemaLocation_); + if (inserts) { + obj = {node: node, 'featureNS': options.featureNS, + 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, + 'srsName': options.srsName}; + ol.object.assign(obj, baseObj); + ol.xml.pushSerializeAndPop(obj, + ol.format.WFS.TRANSACTION_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('Insert'), inserts, + objectStack); + } + if (updates) { + obj = {node: node, 'featureNS': options.featureNS, + 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, + 'srsName': options.srsName}; + ol.object.assign(obj, baseObj); + ol.xml.pushSerializeAndPop(obj, + ol.format.WFS.TRANSACTION_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('Update'), updates, + objectStack); + } + if (deletes) { + ol.xml.pushSerializeAndPop({node: node, 'featureNS': options.featureNS, + 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, + 'srsName': options.srsName}, + ol.format.WFS.TRANSACTION_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('Delete'), deletes, + objectStack); + } + if (options.nativeElements) { + ol.xml.pushSerializeAndPop({node: node, 'featureNS': options.featureNS, + 'featureType': options.featureType, 'featurePrefix': options.featurePrefix, + 'srsName': options.srsName}, + ol.format.WFS.TRANSACTION_SERIALIZERS_, + ol.xml.makeSimpleNodeFactory('Native'), options.nativeElements, + objectStack); + } + return node; +}; + + +/** + * Read the projection from a WFS source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @return {?ol.proj.Projection} Projection. + * @api stable + */ +ol.format.WFS.prototype.readProjection; + + +/** + * @inheritDoc + */ +ol.format.WFS.prototype.readProjectionFromDocument = function(doc) { + goog.asserts.assert(doc.nodeType == Node.DOCUMENT_NODE, + 'doc.nodeType should be a DOCUMENT'); + for (var n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readProjectionFromNode(n); + } + } + return null; +}; + + +/** + * @inheritDoc + */ +ol.format.WFS.prototype.readProjectionFromNode = function(node) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'FeatureCollection', + 'localName should be FeatureCollection'); + + if (node.firstElementChild && + node.firstElementChild.firstElementChild) { + node = node.firstElementChild.firstElementChild; + for (var n = node.firstElementChild; n; n = n.nextElementSibling) { + if (!(n.childNodes.length === 0 || + (n.childNodes.length === 1 && + n.firstChild.nodeType === 3))) { + var objectStack = [{}]; + this.gmlFormat_.readGeometryElement(n, objectStack); + return ol.proj.get(objectStack.pop().srsName); + } + } + } + + return null; +}; + +goog.provide('ol.format.WKT'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.Feature'); +goog.require('ol.format.Feature'); +goog.require('ol.format.TextFeature'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); + + +/** + * @classdesc + * Geometry format for reading and writing data in the `WellKnownText` (WKT) + * format. + * + * @constructor + * @extends {ol.format.TextFeature} + * @param {olx.format.WKTOptions=} opt_options Options. + * @api stable + */ +ol.format.WKT = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.format.TextFeature.call(this); + + /** + * Split GeometryCollection into multiple features. + * @type {boolean} + * @private + */ + this.splitCollection_ = options.splitCollection !== undefined ? + options.splitCollection : false; + +}; +ol.inherits(ol.format.WKT, ol.format.TextFeature); + + +/** + * @const + * @type {string} + */ +ol.format.WKT.EMPTY = 'EMPTY'; + + +/** + * @param {ol.geom.Point} geom Point geometry. + * @return {string} Coordinates part of Point as WKT. + * @private + */ +ol.format.WKT.encodePointGeometry_ = function(geom) { + var coordinates = geom.getCoordinates(); + if (coordinates.length === 0) { + return ''; + } + return coordinates[0] + ' ' + coordinates[1]; +}; + + +/** + * @param {ol.geom.MultiPoint} geom MultiPoint geometry. + * @return {string} Coordinates part of MultiPoint as WKT. + * @private + */ +ol.format.WKT.encodeMultiPointGeometry_ = function(geom) { + var array = []; + var components = geom.getPoints(); + for (var i = 0, ii = components.length; i < ii; ++i) { + array.push('(' + ol.format.WKT.encodePointGeometry_(components[i]) + ')'); + } + return array.join(','); +}; + + +/** + * @param {ol.geom.GeometryCollection} geom GeometryCollection geometry. + * @return {string} Coordinates part of GeometryCollection as WKT. + * @private + */ +ol.format.WKT.encodeGeometryCollectionGeometry_ = function(geom) { + var array = []; + var geoms = geom.getGeometries(); + for (var i = 0, ii = geoms.length; i < ii; ++i) { + array.push(ol.format.WKT.encode_(geoms[i])); + } + return array.join(','); +}; + + +/** + * @param {ol.geom.LineString|ol.geom.LinearRing} geom LineString geometry. + * @return {string} Coordinates part of LineString as WKT. + * @private + */ +ol.format.WKT.encodeLineStringGeometry_ = function(geom) { + var coordinates = geom.getCoordinates(); + var array = []; + for (var i = 0, ii = coordinates.length; i < ii; ++i) { + array.push(coordinates[i][0] + ' ' + coordinates[i][1]); + } + return array.join(','); +}; + + +/** + * @param {ol.geom.MultiLineString} geom MultiLineString geometry. + * @return {string} Coordinates part of MultiLineString as WKT. + * @private + */ +ol.format.WKT.encodeMultiLineStringGeometry_ = function(geom) { + var array = []; + var components = geom.getLineStrings(); + for (var i = 0, ii = components.length; i < ii; ++i) { + array.push('(' + ol.format.WKT.encodeLineStringGeometry_( + components[i]) + ')'); + } + return array.join(','); +}; + + +/** + * @param {ol.geom.Polygon} geom Polygon geometry. + * @return {string} Coordinates part of Polygon as WKT. + * @private + */ +ol.format.WKT.encodePolygonGeometry_ = function(geom) { + var array = []; + var rings = geom.getLinearRings(); + for (var i = 0, ii = rings.length; i < ii; ++i) { + array.push('(' + ol.format.WKT.encodeLineStringGeometry_( + rings[i]) + ')'); + } + return array.join(','); +}; + + +/** + * @param {ol.geom.MultiPolygon} geom MultiPolygon geometry. + * @return {string} Coordinates part of MultiPolygon as WKT. + * @private + */ +ol.format.WKT.encodeMultiPolygonGeometry_ = function(geom) { + var array = []; + var components = geom.getPolygons(); + for (var i = 0, ii = components.length; i < ii; ++i) { + array.push('(' + ol.format.WKT.encodePolygonGeometry_( + components[i]) + ')'); + } + return array.join(','); +}; + + +/** + * Encode a geometry as WKT. + * @param {ol.geom.Geometry} geom The geometry to encode. + * @return {string} WKT string for the geometry. + * @private + */ +ol.format.WKT.encode_ = function(geom) { + var type = geom.getType(); + var geometryEncoder = ol.format.WKT.GeometryEncoder_[type]; + goog.asserts.assert(geometryEncoder, 'geometryEncoder should be defined'); + var enc = geometryEncoder(geom); + type = type.toUpperCase(); + if (enc.length === 0) { + return type + ' ' + ol.format.WKT.EMPTY; + } + return type + '(' + enc + ')'; +}; + + +/** + * @const + * @type {Object.<string, function(ol.geom.Geometry): string>} + * @private + */ +ol.format.WKT.GeometryEncoder_ = { + 'Point': ol.format.WKT.encodePointGeometry_, + 'LineString': ol.format.WKT.encodeLineStringGeometry_, + 'Polygon': ol.format.WKT.encodePolygonGeometry_, + 'MultiPoint': ol.format.WKT.encodeMultiPointGeometry_, + 'MultiLineString': ol.format.WKT.encodeMultiLineStringGeometry_, + 'MultiPolygon': ol.format.WKT.encodeMultiPolygonGeometry_, + 'GeometryCollection': ol.format.WKT.encodeGeometryCollectionGeometry_ +}; + + +/** + * Parse a WKT string. + * @param {string} wkt WKT string. + * @return {ol.geom.Geometry|ol.geom.GeometryCollection|undefined} + * The geometry created. + * @private + */ +ol.format.WKT.prototype.parse_ = function(wkt) { + var lexer = new ol.format.WKT.Lexer(wkt); + var parser = new ol.format.WKT.Parser(lexer); + return parser.parse(); +}; + + +/** + * Read a feature from a WKT source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.Feature} Feature. + * @api stable + */ +ol.format.WKT.prototype.readFeature; + + +/** + * @inheritDoc + */ +ol.format.WKT.prototype.readFeatureFromText = function(text, opt_options) { + var geom = this.readGeometryFromText(text, opt_options); + if (geom) { + var feature = new ol.Feature(); + feature.setGeometry(geom); + return feature; + } + return null; +}; + + +/** + * Read all features from a WKT source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.WKT.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.WKT.prototype.readFeaturesFromText = function(text, opt_options) { + var geometries = []; + var geometry = this.readGeometryFromText(text, opt_options); + if (this.splitCollection_ && + geometry.getType() == ol.geom.GeometryType.GEOMETRY_COLLECTION) { + geometries = (/** @type {ol.geom.GeometryCollection} */ (geometry)) + .getGeometriesArray(); + } else { + geometries = [geometry]; + } + var feature, features = []; + for (var i = 0, ii = geometries.length; i < ii; ++i) { + feature = new ol.Feature(); + feature.setGeometry(geometries[i]); + features.push(feature); + } + return features; +}; + + +/** + * Read a single geometry from a WKT source. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Read options. + * @return {ol.geom.Geometry} Geometry. + * @api stable + */ +ol.format.WKT.prototype.readGeometry; + + +/** + * @inheritDoc + */ +ol.format.WKT.prototype.readGeometryFromText = function(text, opt_options) { + var geometry = this.parse_(text); + if (geometry) { + return /** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions(geometry, false, opt_options)); + } else { + return null; + } +}; + + +/** + * Encode a feature as a WKT string. + * + * @function + * @param {ol.Feature} feature Feature. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} WKT string. + * @api stable + */ +ol.format.WKT.prototype.writeFeature; + + +/** + * @inheritDoc + */ +ol.format.WKT.prototype.writeFeatureText = function(feature, opt_options) { + var geometry = feature.getGeometry(); + if (geometry) { + return this.writeGeometryText(geometry, opt_options); + } + return ''; +}; + + +/** + * Encode an array of features as a WKT string. + * + * @function + * @param {Array.<ol.Feature>} features Features. + * @param {olx.format.WriteOptions=} opt_options Write options. + * @return {string} WKT string. + * @api stable + */ +ol.format.WKT.prototype.writeFeatures; + + +/** + * @inheritDoc + */ +ol.format.WKT.prototype.writeFeaturesText = function(features, opt_options) { + if (features.length == 1) { + return this.writeFeatureText(features[0], opt_options); + } + var geometries = []; + for (var i = 0, ii = features.length; i < ii; ++i) { + geometries.push(features[i].getGeometry()); + } + var collection = new ol.geom.GeometryCollection(geometries); + return this.writeGeometryText(collection, opt_options); +}; + + +/** + * Write a single geometry as a WKT string. + * + * @function + * @param {ol.geom.Geometry} geometry Geometry. + * @return {string} WKT string. + * @api stable + */ +ol.format.WKT.prototype.writeGeometry; + + +/** + * @inheritDoc + */ +ol.format.WKT.prototype.writeGeometryText = function(geometry, opt_options) { + return ol.format.WKT.encode_(/** @type {ol.geom.Geometry} */ ( + ol.format.Feature.transformWithOptions(geometry, true, opt_options))); +}; + + +/** + * @const + * @enum {number} + */ +ol.format.WKT.TokenType = { + TEXT: 1, + LEFT_PAREN: 2, + RIGHT_PAREN: 3, + NUMBER: 4, + COMMA: 5, + EOF: 6 +}; + + +/** + * Class to tokenize a WKT string. + * @param {string} wkt WKT string. + * @constructor + * @protected + */ +ol.format.WKT.Lexer = function(wkt) { + + /** + * @type {string} + */ + this.wkt = wkt; + + /** + * @type {number} + * @private + */ + this.index_ = -1; +}; + + +/** + * @param {string} c Character. + * @return {boolean} Whether the character is alphabetic. + * @private + */ +ol.format.WKT.Lexer.prototype.isAlpha_ = function(c) { + return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z'; +}; + + +/** + * @param {string} c Character. + * @param {boolean=} opt_decimal Whether the string number + * contains a dot, i.e. is a decimal number. + * @return {boolean} Whether the character is numeric. + * @private + */ +ol.format.WKT.Lexer.prototype.isNumeric_ = function(c, opt_decimal) { + var decimal = opt_decimal !== undefined ? opt_decimal : false; + return c >= '0' && c <= '9' || c == '.' && !decimal; +}; + + +/** + * @param {string} c Character. + * @return {boolean} Whether the character is whitespace. + * @private + */ +ol.format.WKT.Lexer.prototype.isWhiteSpace_ = function(c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +}; + + +/** + * @return {string} Next string character. + * @private + */ +ol.format.WKT.Lexer.prototype.nextChar_ = function() { + return this.wkt.charAt(++this.index_); +}; + + +/** + * Fetch and return the next token. + * @return {!ol.WKTToken} Next string token. + */ +ol.format.WKT.Lexer.prototype.nextToken = function() { + var c = this.nextChar_(); + var token = {position: this.index_, value: c}; + + if (c == '(') { + token.type = ol.format.WKT.TokenType.LEFT_PAREN; + } else if (c == ',') { + token.type = ol.format.WKT.TokenType.COMMA; + } else if (c == ')') { + token.type = ol.format.WKT.TokenType.RIGHT_PAREN; + } else if (this.isNumeric_(c) || c == '-') { + token.type = ol.format.WKT.TokenType.NUMBER; + token.value = this.readNumber_(); + } else if (this.isAlpha_(c)) { + token.type = ol.format.WKT.TokenType.TEXT; + token.value = this.readText_(); + } else if (this.isWhiteSpace_(c)) { + return this.nextToken(); + } else if (c === '') { + token.type = ol.format.WKT.TokenType.EOF; + } else { + throw new Error('Unexpected character: ' + c); + } + + return token; +}; + + +/** + * @return {number} Numeric token value. + * @private + */ +ol.format.WKT.Lexer.prototype.readNumber_ = function() { + var c, index = this.index_; + var decimal = false; + var scientificNotation = false; + do { + if (c == '.') { + decimal = true; + } else if (c == 'e' || c == 'E') { + scientificNotation = true; + } + c = this.nextChar_(); + } while ( + this.isNumeric_(c, decimal) || + // if we haven't detected a scientific number before, 'e' or 'E' + // hint that we should continue to read + !scientificNotation && (c == 'e' || c == 'E') || + // once we know that we have a scientific number, both '-' and '+' + // are allowed + scientificNotation && (c == '-' || c == '+') + ); + return parseFloat(this.wkt.substring(index, this.index_--)); +}; + + +/** + * @return {string} String token value. + * @private + */ +ol.format.WKT.Lexer.prototype.readText_ = function() { + var c, index = this.index_; + do { + c = this.nextChar_(); + } while (this.isAlpha_(c)); + return this.wkt.substring(index, this.index_--).toUpperCase(); +}; + + +/** + * Class to parse the tokens from the WKT string. + * @param {ol.format.WKT.Lexer} lexer The lexer. + * @constructor + * @protected + */ +ol.format.WKT.Parser = function(lexer) { + + /** + * @type {ol.format.WKT.Lexer} + * @private + */ + this.lexer_ = lexer; + + /** + * @type {ol.WKTToken} + * @private + */ + this.token_; + + /** + * @type {number} + * @private + */ + this.dimension_ = 2; +}; + + +/** + * Fetch the next token form the lexer and replace the active token. + * @private + */ +ol.format.WKT.Parser.prototype.consume_ = function() { + this.token_ = this.lexer_.nextToken(); +}; + + +/** + * If the given type matches the current token, consume it. + * @param {ol.format.WKT.TokenType} type Token type. + * @return {boolean} Whether the token matches the given type. + */ +ol.format.WKT.Parser.prototype.match = function(type) { + var isMatch = this.token_.type == type; + if (isMatch) { + this.consume_(); + } + return isMatch; +}; + + +/** + * Try to parse the tokens provided by the lexer. + * @return {ol.geom.Geometry|ol.geom.GeometryCollection} The geometry. + */ +ol.format.WKT.Parser.prototype.parse = function() { + this.consume_(); + var geometry = this.parseGeometry_(); + goog.asserts.assert(this.token_.type == ol.format.WKT.TokenType.EOF, + 'token type should be end of file'); + return geometry; +}; + + +/** + * @return {!(ol.geom.Geometry|ol.geom.GeometryCollection)} The geometry. + * @private + */ +ol.format.WKT.Parser.prototype.parseGeometry_ = function() { + var token = this.token_; + if (this.match(ol.format.WKT.TokenType.TEXT)) { + var geomType = token.value; + if (geomType == ol.geom.GeometryType.GEOMETRY_COLLECTION.toUpperCase()) { + var geometries = this.parseGeometryCollectionText_(); + return new ol.geom.GeometryCollection(geometries); + } else { + var parser = ol.format.WKT.Parser.GeometryParser_[geomType]; + var ctor = ol.format.WKT.Parser.GeometryConstructor_[geomType]; + if (!parser || !ctor) { + throw new Error('Invalid geometry type: ' + geomType); + } + var coordinates = parser.call(this); + return new ctor(coordinates); + } + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<ol.geom.Geometry>} A collection of geometries. + * @private + */ +ol.format.WKT.Parser.prototype.parseGeometryCollectionText_ = function() { + if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) { + var geometries = []; + do { + geometries.push(this.parseGeometry_()); + } while (this.match(ol.format.WKT.TokenType.COMMA)); + if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) { + return geometries; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {Array.<number>} All values in a point. + * @private + */ +ol.format.WKT.Parser.prototype.parsePointText_ = function() { + if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) { + var coordinates = this.parsePoint_(); + if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return null; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<!Array.<number>>} All points in a linestring. + * @private + */ +ol.format.WKT.Parser.prototype.parseLineStringText_ = function() { + if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) { + var coordinates = this.parsePointList_(); + if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<!Array.<number>>} All points in a polygon. + * @private + */ +ol.format.WKT.Parser.prototype.parsePolygonText_ = function() { + if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) { + var coordinates = this.parseLineStringTextList_(); + if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<!Array.<number>>} All points in a multipoint. + * @private + */ +ol.format.WKT.Parser.prototype.parseMultiPointText_ = function() { + if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) { + var coordinates; + if (this.token_.type == ol.format.WKT.TokenType.LEFT_PAREN) { + coordinates = this.parsePointTextList_(); + } else { + coordinates = this.parsePointList_(); + } + if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<!Array.<number>>} All linestring points + * in a multilinestring. + * @private + */ +ol.format.WKT.Parser.prototype.parseMultiLineStringText_ = function() { + if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) { + var coordinates = this.parseLineStringTextList_(); + if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<!Array.<number>>} All polygon points in a multipolygon. + * @private + */ +ol.format.WKT.Parser.prototype.parseMultiPolygonText_ = function() { + if (this.match(ol.format.WKT.TokenType.LEFT_PAREN)) { + var coordinates = this.parsePolygonTextList_(); + if (this.match(ol.format.WKT.TokenType.RIGHT_PAREN)) { + return coordinates; + } + } else if (this.isEmptyGeometry_()) { + return []; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<number>} A point. + * @private + */ +ol.format.WKT.Parser.prototype.parsePoint_ = function() { + var coordinates = []; + for (var i = 0; i < this.dimension_; ++i) { + var token = this.token_; + if (this.match(ol.format.WKT.TokenType.NUMBER)) { + coordinates.push(token.value); + } else { + break; + } + } + if (coordinates.length == this.dimension_) { + return coordinates; + } + throw new Error(this.formatErrorMessage_()); +}; + + +/** + * @return {!Array.<!Array.<number>>} An array of points. + * @private + */ +ol.format.WKT.Parser.prototype.parsePointList_ = function() { + var coordinates = [this.parsePoint_()]; + while (this.match(ol.format.WKT.TokenType.COMMA)) { + coordinates.push(this.parsePoint_()); + } + return coordinates; +}; + + +/** + * @return {!Array.<!Array.<number>>} An array of points. + * @private + */ +ol.format.WKT.Parser.prototype.parsePointTextList_ = function() { + var coordinates = [this.parsePointText_()]; + while (this.match(ol.format.WKT.TokenType.COMMA)) { + coordinates.push(this.parsePointText_()); + } + return coordinates; +}; + + +/** + * @return {!Array.<!Array.<number>>} An array of points. + * @private + */ +ol.format.WKT.Parser.prototype.parseLineStringTextList_ = function() { + var coordinates = [this.parseLineStringText_()]; + while (this.match(ol.format.WKT.TokenType.COMMA)) { + coordinates.push(this.parseLineStringText_()); + } + return coordinates; +}; + + +/** + * @return {!Array.<!Array.<number>>} An array of points. + * @private + */ +ol.format.WKT.Parser.prototype.parsePolygonTextList_ = function() { + var coordinates = [this.parsePolygonText_()]; + while (this.match(ol.format.WKT.TokenType.COMMA)) { + coordinates.push(this.parsePolygonText_()); + } + return coordinates; +}; + + +/** + * @return {boolean} Whether the token implies an empty geometry. + * @private + */ +ol.format.WKT.Parser.prototype.isEmptyGeometry_ = function() { + var isEmpty = this.token_.type == ol.format.WKT.TokenType.TEXT && + this.token_.value == ol.format.WKT.EMPTY; + if (isEmpty) { + this.consume_(); + } + return isEmpty; +}; + + +/** + * Create an error message for an unexpected token error. + * @return {string} Error message. + * @private + */ +ol.format.WKT.Parser.prototype.formatErrorMessage_ = function() { + return 'Unexpected `' + this.token_.value + '` at position ' + + this.token_.position + ' in `' + this.lexer_.wkt + '`'; +}; + + +/** + * @enum {function (new:ol.geom.Geometry, Array, ol.geom.GeometryLayout)} + * @private + */ +ol.format.WKT.Parser.GeometryConstructor_ = { + 'POINT': ol.geom.Point, + 'LINESTRING': ol.geom.LineString, + 'POLYGON': ol.geom.Polygon, + 'MULTIPOINT': ol.geom.MultiPoint, + 'MULTILINESTRING': ol.geom.MultiLineString, + 'MULTIPOLYGON': ol.geom.MultiPolygon +}; + + +/** + * @enum {(function(): Array)} + * @private + */ +ol.format.WKT.Parser.GeometryParser_ = { + 'POINT': ol.format.WKT.Parser.prototype.parsePointText_, + 'LINESTRING': ol.format.WKT.Parser.prototype.parseLineStringText_, + 'POLYGON': ol.format.WKT.Parser.prototype.parsePolygonText_, + 'MULTIPOINT': ol.format.WKT.Parser.prototype.parseMultiPointText_, + 'MULTILINESTRING': ol.format.WKT.Parser.prototype.parseMultiLineStringText_, + 'MULTIPOLYGON': ol.format.WKT.Parser.prototype.parseMultiPolygonText_ +}; + +goog.provide('ol.format.WMSCapabilities'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.format.XLink'); +goog.require('ol.format.XML'); +goog.require('ol.format.XSD'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Format for reading WMS capabilities data + * + * @constructor + * @extends {ol.format.XML} + * @api + */ +ol.format.WMSCapabilities = function() { + + ol.format.XML.call(this); + + /** + * @type {string|undefined} + */ + this.version = undefined; +}; +ol.inherits(ol.format.WMSCapabilities, ol.format.XML); + + +/** + * Read a WMS capabilities document. + * + * @function + * @param {Document|Node|string} source The XML source. + * @return {Object} An object representing the WMS capabilities. + * @api + */ +ol.format.WMSCapabilities.prototype.read; + + +/** + * @param {Document} doc Document. + * @return {Object} WMS Capability object. + */ +ol.format.WMSCapabilities.prototype.readFromDocument = function(doc) { + goog.asserts.assert(doc.nodeType == Node.DOCUMENT_NODE, + 'doc.nodeType should be DOCUMENT'); + for (var n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFromNode(n); + } + } + return null; +}; + + +/** + * @param {Node} node Node. + * @return {Object} WMS Capability object. + */ +ol.format.WMSCapabilities.prototype.readFromNode = function(node) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'WMS_Capabilities' || + node.localName == 'WMT_MS_Capabilities', + 'localName should be WMS_Capabilities or WMT_MS_Capabilities'); + this.version = node.getAttribute('version').trim(); + goog.asserts.assertString(this.version, 'this.version should be a string'); + var wmsCapabilityObject = ol.xml.pushParseAndPop({ + 'version': this.version + }, ol.format.WMSCapabilities.PARSERS_, node, []); + return wmsCapabilityObject ? wmsCapabilityObject : null; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Attribution object. + */ +ol.format.WMSCapabilities.readAttribution_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Attribution', + 'localName should be Attribution'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object} Bounding box object. + */ +ol.format.WMSCapabilities.readBoundingBox_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'BoundingBox', + 'localName should be BoundingBox'); + + var extent = [ + ol.format.XSD.readDecimalString(node.getAttribute('minx')), + ol.format.XSD.readDecimalString(node.getAttribute('miny')), + ol.format.XSD.readDecimalString(node.getAttribute('maxx')), + ol.format.XSD.readDecimalString(node.getAttribute('maxy')) + ]; + + var resolutions = [ + ol.format.XSD.readDecimalString(node.getAttribute('resx')), + ol.format.XSD.readDecimalString(node.getAttribute('resy')) + ]; + + return { + 'crs': node.getAttribute('CRS'), + 'extent': extent, + 'res': resolutions + }; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {ol.Extent|undefined} Bounding box object. + */ +ol.format.WMSCapabilities.readEXGeographicBoundingBox_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'EX_GeographicBoundingBox', + 'localName should be EX_GeographicBoundingBox'); + var geographicBoundingBox = ol.xml.pushParseAndPop( + {}, + ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_, + node, objectStack); + if (!geographicBoundingBox) { + return undefined; + } + var westBoundLongitude = /** @type {number|undefined} */ + (geographicBoundingBox['westBoundLongitude']); + var southBoundLatitude = /** @type {number|undefined} */ + (geographicBoundingBox['southBoundLatitude']); + var eastBoundLongitude = /** @type {number|undefined} */ + (geographicBoundingBox['eastBoundLongitude']); + var northBoundLatitude = /** @type {number|undefined} */ + (geographicBoundingBox['northBoundLatitude']); + if (westBoundLongitude === undefined || southBoundLatitude === undefined || + eastBoundLongitude === undefined || northBoundLatitude === undefined) { + return undefined; + } + return [ + westBoundLongitude, southBoundLatitude, + eastBoundLongitude, northBoundLatitude + ]; +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} Capability object. + */ +ol.format.WMSCapabilities.readCapability_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Capability', + 'localName should be Capability'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.CAPABILITY_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} Service object. + */ +ol.format.WMSCapabilities.readService_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Service', + 'localName should be Service'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.SERVICE_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} Contact information object. + */ +ol.format.WMSCapabilities.readContactInformation_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType shpuld be ELEMENT'); + goog.asserts.assert(node.localName == 'ContactInformation', + 'localName should be ContactInformation'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_, + node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} Contact person object. + */ +ol.format.WMSCapabilities.readContactPersonPrimary_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'ContactPersonPrimary', + 'localName should be ContactPersonPrimary'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_, + node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} Contact address object. + */ +ol.format.WMSCapabilities.readContactAddress_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'ContactAddress', + 'localName should be ContactAddress'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_, + node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Array.<string>|undefined} Format array. + */ +ol.format.WMSCapabilities.readException_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Exception', + 'localName should be Exception'); + return ol.xml.pushParseAndPop( + [], ol.format.WMSCapabilities.EXCEPTION_PARSERS_, node, objectStack); +}; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @private + * @return {Object|undefined} Layer object. + */ +ol.format.WMSCapabilities.readCapabilityLayer_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Layer', 'localName should be Layer'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Layer object. + */ +ol.format.WMSCapabilities.readLayer_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Layer', 'localName should be Layer'); + var parentLayerObject = /** @type {Object.<string,*>} */ + (objectStack[objectStack.length - 1]); + + var layerObject = ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.LAYER_PARSERS_, node, objectStack); + + if (!layerObject) { + return undefined; + } + var queryable = + ol.format.XSD.readBooleanString(node.getAttribute('queryable')); + if (queryable === undefined) { + queryable = parentLayerObject['queryable']; + } + layerObject['queryable'] = queryable !== undefined ? queryable : false; + + var cascaded = ol.format.XSD.readNonNegativeIntegerString( + node.getAttribute('cascaded')); + if (cascaded === undefined) { + cascaded = parentLayerObject['cascaded']; + } + layerObject['cascaded'] = cascaded; + + var opaque = ol.format.XSD.readBooleanString(node.getAttribute('opaque')); + if (opaque === undefined) { + opaque = parentLayerObject['opaque']; + } + layerObject['opaque'] = opaque !== undefined ? opaque : false; + + var noSubsets = + ol.format.XSD.readBooleanString(node.getAttribute('noSubsets')); + if (noSubsets === undefined) { + noSubsets = parentLayerObject['noSubsets']; + } + layerObject['noSubsets'] = noSubsets !== undefined ? noSubsets : false; + + var fixedWidth = + ol.format.XSD.readDecimalString(node.getAttribute('fixedWidth')); + if (!fixedWidth) { + fixedWidth = parentLayerObject['fixedWidth']; + } + layerObject['fixedWidth'] = fixedWidth; + + var fixedHeight = + ol.format.XSD.readDecimalString(node.getAttribute('fixedHeight')); + if (!fixedHeight) { + fixedHeight = parentLayerObject['fixedHeight']; + } + layerObject['fixedHeight'] = fixedHeight; + + // See 7.2.4.8 + var addKeys = ['Style', 'CRS', 'AuthorityURL']; + addKeys.forEach(function(key) { + if (key in parentLayerObject) { + var childValue = layerObject[key] || []; + layerObject[key] = childValue.concat(parentLayerObject[key]); + } + }); + + var replaceKeys = ['EX_GeographicBoundingBox', 'BoundingBox', 'Dimension', + 'Attribution', 'MinScaleDenominator', 'MaxScaleDenominator']; + replaceKeys.forEach(function(key) { + if (!(key in layerObject)) { + var parentValue = parentLayerObject[key]; + layerObject[key] = parentValue; + } + }); + + return layerObject; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object} Dimension object. + */ +ol.format.WMSCapabilities.readDimension_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Dimension', + 'localName should be Dimension'); + var dimensionObject = { + 'name': node.getAttribute('name'), + 'units': node.getAttribute('units'), + 'unitSymbol': node.getAttribute('unitSymbol'), + 'default': node.getAttribute('default'), + 'multipleValues': ol.format.XSD.readBooleanString( + node.getAttribute('multipleValues')), + 'nearestValue': ol.format.XSD.readBooleanString( + node.getAttribute('nearestValue')), + 'current': ol.format.XSD.readBooleanString(node.getAttribute('current')), + 'values': ol.format.XSD.readString(node) + }; + return dimensionObject; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Online resource object. + */ +ol.format.WMSCapabilities.readFormatOnlineresource_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_, + node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Request object. + */ +ol.format.WMSCapabilities.readRequest_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Request', + 'localName should be Request'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.REQUEST_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} DCP type object. + */ +ol.format.WMSCapabilities.readDCPType_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'DCPType', + 'localName should be DCPType'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.DCPTYPE_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} HTTP object. + */ +ol.format.WMSCapabilities.readHTTP_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'HTTP', 'localName should be HTTP'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.HTTP_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Operation type object. + */ +ol.format.WMSCapabilities.readOperationType_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Online resource object. + */ +ol.format.WMSCapabilities.readSizedFormatOnlineresource_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + var formatOnlineresource = + ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack); + if (formatOnlineresource) { + var size = [ + ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('width')), + ol.format.XSD.readNonNegativeIntegerString(node.getAttribute('height')) + ]; + formatOnlineresource['size'] = size; + return formatOnlineresource; + } + return undefined; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Authority URL object. + */ +ol.format.WMSCapabilities.readAuthorityURL_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'AuthorityURL', + 'localName should be AuthorityURL'); + var authorityObject = + ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack); + if (authorityObject) { + authorityObject['name'] = node.getAttribute('name'); + return authorityObject; + } + return undefined; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Metadata URL object. + */ +ol.format.WMSCapabilities.readMetadataURL_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'MetadataURL', + 'localName should be MetadataURL'); + var metadataObject = + ol.format.WMSCapabilities.readFormatOnlineresource_(node, objectStack); + if (metadataObject) { + metadataObject['type'] = node.getAttribute('type'); + return metadataObject; + } + return undefined; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Style object. + */ +ol.format.WMSCapabilities.readStyle_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Style', 'localName should be Style'); + return ol.xml.pushParseAndPop( + {}, ol.format.WMSCapabilities.STYLE_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Array.<string>|undefined} Keyword list. + */ +ol.format.WMSCapabilities.readKeywordList_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'KeywordList', + 'localName should be KeywordList'); + return ol.xml.pushParseAndPop( + [], ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_, node, objectStack); +}; + + +/** + * @const + * @private + * @type {Array.<string>} + */ +ol.format.WMSCapabilities.NAMESPACE_URIS_ = [ + null, + 'http://www.opengis.net/wms' +]; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Service': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readService_), + 'Capability': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readCapability_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.CAPABILITY_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Request': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readRequest_), + 'Exception': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readException_), + 'Layer': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readCapabilityLayer_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.SERVICE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'KeywordList': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readKeywordList_), + 'OnlineResource': ol.xml.makeObjectPropertySetter( + ol.format.XLink.readHref), + 'ContactInformation': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readContactInformation_), + 'Fees': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'AccessConstraints': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'LayerLimit': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'MaxWidth': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'MaxHeight': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.CONTACT_INFORMATION_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'ContactPersonPrimary': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readContactPersonPrimary_), + 'ContactPosition': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'ContactAddress': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readContactAddress_), + 'ContactVoiceTelephone': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'ContactFacsimileTelephone': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'ContactElectronicMailAddress': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.CONTACT_PERSON_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'ContactPerson': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'ContactOrganization': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.CONTACT_ADDRESS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'AddressType': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Address': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'City': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'StateOrProvince': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'PostCode': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Country': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.EXCEPTION_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Format': ol.xml.makeArrayPusher(ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.LAYER_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'KeywordList': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readKeywordList_), + 'CRS': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString), + 'EX_GeographicBoundingBox': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readEXGeographicBoundingBox_), + 'BoundingBox': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readBoundingBox_), + 'Dimension': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readDimension_), + 'Attribution': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readAttribution_), + 'AuthorityURL': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readAuthorityURL_), + 'Identifier': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString), + 'MetadataURL': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readMetadataURL_), + 'DataURL': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readFormatOnlineresource_), + 'FeatureListURL': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readFormatOnlineresource_), + 'Style': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readStyle_), + 'MinScaleDenominator': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readDecimal), + 'MaxScaleDenominator': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readDecimal), + 'Layer': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readLayer_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.ATTRIBUTION_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'OnlineResource': ol.xml.makeObjectPropertySetter( + ol.format.XLink.readHref), + 'LogoURL': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readSizedFormatOnlineresource_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.EX_GEOGRAPHIC_BOUNDING_BOX_PARSERS_ = + ol.xml.makeStructureNS(ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'westBoundLongitude': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readDecimal), + 'eastBoundLongitude': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readDecimal), + 'southBoundLatitude': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readDecimal), + 'northBoundLatitude': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readDecimal) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.REQUEST_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'GetCapabilities': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readOperationType_), + 'GetMap': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readOperationType_), + 'GetFeatureInfo': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readOperationType_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.OPERATIONTYPE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Format': ol.xml.makeObjectPropertyPusher(ol.format.XSD.readString), + 'DCPType': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readDCPType_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.DCPTYPE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'HTTP': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readHTTP_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.HTTP_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Get': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readFormatOnlineresource_), + 'Post': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readFormatOnlineresource_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.STYLE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Name': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Title': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'Abstract': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'LegendURL': ol.xml.makeObjectPropertyPusher( + ol.format.WMSCapabilities.readSizedFormatOnlineresource_), + 'StyleSheetURL': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readFormatOnlineresource_), + 'StyleURL': ol.xml.makeObjectPropertySetter( + ol.format.WMSCapabilities.readFormatOnlineresource_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.FORMAT_ONLINERESOURCE_PARSERS_ = + ol.xml.makeStructureNS(ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Format': ol.xml.makeObjectPropertySetter(ol.format.XSD.readString), + 'OnlineResource': ol.xml.makeObjectPropertySetter( + ol.format.XLink.readHref) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMSCapabilities.KEYWORDLIST_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMSCapabilities.NAMESPACE_URIS_, { + 'Keyword': ol.xml.makeArrayPusher(ol.format.XSD.readString) + }); + +goog.provide('ol.format.WMSGetFeatureInfo'); + +goog.require('goog.asserts'); +goog.require('ol.array'); +goog.require('ol.format.GML2'); +goog.require('ol.format.XMLFeature'); +goog.require('ol.object'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Format for reading WMSGetFeatureInfo format. It uses + * {@link ol.format.GML2} to read features. + * + * @constructor + * @extends {ol.format.XMLFeature} + * @param {olx.format.WMSGetFeatureInfoOptions=} opt_options Options. + * @api + */ +ol.format.WMSGetFeatureInfo = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {string} + */ + this.featureNS_ = 'http://mapserver.gis.umn.edu/mapserver'; + + + /** + * @private + * @type {ol.format.GML2} + */ + this.gmlFormat_ = new ol.format.GML2(); + + + /** + * @private + * @type {Array.<string>} + */ + this.layers_ = options.layers ? options.layers : null; + + ol.format.XMLFeature.call(this); +}; +ol.inherits(ol.format.WMSGetFeatureInfo, ol.format.XMLFeature); + + +/** + * @const + * @type {string} + * @private + */ +ol.format.WMSGetFeatureInfo.featureIdentifier_ = '_feature'; + + +/** + * @const + * @type {string} + * @private + */ +ol.format.WMSGetFeatureInfo.layerIdentifier_ = '_layer'; + + +/** + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Array.<ol.Feature>} Features. + * @private + */ +ol.format.WMSGetFeatureInfo.prototype.readFeatures_ = function(node, objectStack) { + + node.setAttribute('namespaceURI', this.featureNS_); + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + var localName = node.localName; + /** @type {Array.<ol.Feature>} */ + var features = []; + if (node.childNodes.length === 0) { + return features; + } + if (localName == 'msGMLOutput') { + for (var i = 0, ii = node.childNodes.length; i < ii; i++) { + var layer = node.childNodes[i]; + if (layer.nodeType !== Node.ELEMENT_NODE) { + continue; + } + var context = objectStack[0]; + goog.asserts.assert(goog.isObject(context), + 'context should be an Object'); + + goog.asserts.assert(layer.localName.indexOf( + ol.format.WMSGetFeatureInfo.layerIdentifier_) >= 0, + 'localName of layer node should match layerIdentifier'); + + var toRemove = ol.format.WMSGetFeatureInfo.layerIdentifier_; + var layerName = layer.localName.replace(toRemove, ''); + + if (this.layers_ && !ol.array.includes(this.layers_, layerName)) { + continue; + } + + var featureType = layerName + + ol.format.WMSGetFeatureInfo.featureIdentifier_; + + context['featureType'] = featureType; + context['featureNS'] = this.featureNS_; + + var parsers = {}; + parsers[featureType] = ol.xml.makeArrayPusher( + this.gmlFormat_.readFeatureElement, this.gmlFormat_); + var parsersNS = ol.xml.makeStructureNS( + [context['featureNS'], null], parsers); + layer.setAttribute('namespaceURI', this.featureNS_); + var layerFeatures = ol.xml.pushParseAndPop( + [], parsersNS, layer, objectStack, this.gmlFormat_); + if (layerFeatures) { + ol.array.extend(features, layerFeatures); + } + } + } + if (localName == 'FeatureCollection') { + var gmlFeatures = ol.xml.pushParseAndPop([], + this.gmlFormat_.FEATURE_COLLECTION_PARSERS, node, + [{}], this.gmlFormat_); + if (gmlFeatures) { + features = gmlFeatures; + } + } + return features; +}; + + +/** + * Read all features from a WMSGetFeatureInfo response. + * + * @function + * @param {Document|Node|Object|string} source Source. + * @param {olx.format.ReadOptions=} opt_options Options. + * @return {Array.<ol.Feature>} Features. + * @api stable + */ +ol.format.WMSGetFeatureInfo.prototype.readFeatures; + + +/** + * @inheritDoc + */ +ol.format.WMSGetFeatureInfo.prototype.readFeaturesFromNode = function(node, opt_options) { + var options = {}; + if (opt_options) { + ol.object.assign(options, this.getReadOptions(node, opt_options)); + } + return this.readFeatures_(node, [options]); +}; + +goog.provide('ol.format.WMTSCapabilities'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.format.OWS'); +goog.require('ol.format.XLink'); +goog.require('ol.format.XML'); +goog.require('ol.format.XSD'); +goog.require('ol.xml'); + + +/** + * @classdesc + * Format for reading WMTS capabilities data. + * + * @constructor + * @extends {ol.format.XML} + * @api + */ +ol.format.WMTSCapabilities = function() { + ol.format.XML.call(this); + + /** + * @type {ol.format.OWS} + * @private + */ + this.owsParser_ = new ol.format.OWS(); +}; +ol.inherits(ol.format.WMTSCapabilities, ol.format.XML); + + +/** + * Read a WMTS capabilities document. + * + * @function + * @param {Document|Node|string} source The XML source. + * @return {Object} An object representing the WMTS capabilities. + * @api + */ +ol.format.WMTSCapabilities.prototype.read; + + +/** + * @param {Document} doc Document. + * @return {Object} WMTS Capability object. + */ +ol.format.WMTSCapabilities.prototype.readFromDocument = function(doc) { + goog.asserts.assert(doc.nodeType == Node.DOCUMENT_NODE, + 'doc.nodeType should be DOCUMENT'); + for (var n = doc.firstChild; n; n = n.nextSibling) { + if (n.nodeType == Node.ELEMENT_NODE) { + return this.readFromNode(n); + } + } + return null; +}; + + +/** + * @param {Node} node Node. + * @return {Object} WMTS Capability object. + */ +ol.format.WMTSCapabilities.prototype.readFromNode = function(node) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Capabilities', + 'localName should be Capabilities'); + var version = node.getAttribute('version').trim(); + goog.asserts.assertString(version, 'version should be a string'); + var WMTSCapabilityObject = this.owsParser_.readFromNode(node); + if (!WMTSCapabilityObject) { + return null; + } + WMTSCapabilityObject['version'] = version; + WMTSCapabilityObject = ol.xml.pushParseAndPop(WMTSCapabilityObject, + ol.format.WMTSCapabilities.PARSERS_, node, []); + return WMTSCapabilityObject ? WMTSCapabilityObject : null; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Attribution object. + */ +ol.format.WMTSCapabilities.readContents_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Contents', + 'localName should be Contents'); + + return ol.xml.pushParseAndPop({}, + ol.format.WMTSCapabilities.CONTENTS_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Layers object. + */ +ol.format.WMTSCapabilities.readLayer_ = function(node, objectStack) { + goog.asserts.assert(node.nodeType == Node.ELEMENT_NODE, + 'node.nodeType should be ELEMENT'); + goog.asserts.assert(node.localName == 'Layer', 'localName should be Layer'); + return ol.xml.pushParseAndPop({}, + ol.format.WMTSCapabilities.LAYER_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Tile Matrix Set object. + */ +ol.format.WMTSCapabilities.readTileMatrixSet_ = function(node, objectStack) { + return ol.xml.pushParseAndPop({}, + ol.format.WMTSCapabilities.TMS_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Style object. + */ +ol.format.WMTSCapabilities.readStyle_ = function(node, objectStack) { + var style = ol.xml.pushParseAndPop({}, + ol.format.WMTSCapabilities.STYLE_PARSERS_, node, objectStack); + if (!style) { + return undefined; + } + var isDefault = node.getAttribute('isDefault') === 'true'; + style['isDefault'] = isDefault; + return style; + +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Tile Matrix Set Link object. + */ +ol.format.WMTSCapabilities.readTileMatrixSetLink_ = function(node, + objectStack) { + return ol.xml.pushParseAndPop({}, + ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Dimension object. + */ +ol.format.WMTSCapabilities.readDimensions_ = function(node, objectStack) { + return ol.xml.pushParseAndPop({}, + ol.format.WMTSCapabilities.DIMENSION_PARSERS_, node, objectStack); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Resource URL object. + */ +ol.format.WMTSCapabilities.readResourceUrl_ = function(node, objectStack) { + var format = node.getAttribute('format'); + var template = node.getAttribute('template'); + var resourceType = node.getAttribute('resourceType'); + var resource = {}; + if (format) { + resource['format'] = format; + } + if (template) { + resource['template'] = template; + } + if (resourceType) { + resource['resourceType'] = resourceType; + } + return resource; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} WGS84 BBox object. + */ +ol.format.WMTSCapabilities.readWgs84BoundingBox_ = function(node, objectStack) { + var coordinates = ol.xml.pushParseAndPop([], + ol.format.WMTSCapabilities.WGS84_BBOX_READERS_, node, objectStack); + if (coordinates.length != 2) { + return undefined; + } + return ol.extent.boundingExtent(coordinates); +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Legend object. + */ +ol.format.WMTSCapabilities.readLegendUrl_ = function(node, objectStack) { + var legend = {}; + legend['format'] = node.getAttribute('format'); + legend['href'] = ol.format.XLink.readHref(node); + return legend; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} Coordinates object. + */ +ol.format.WMTSCapabilities.readCoordinates_ = function(node, objectStack) { + var coordinates = ol.format.XSD.readString(node).split(' '); + if (!coordinates || coordinates.length != 2) { + return undefined; + } + var x = +coordinates[0]; + var y = +coordinates[1]; + if (isNaN(x) || isNaN(y)) { + return undefined; + } + return [x, y]; +}; + + +/** + * @private + * @param {Node} node Node. + * @param {Array.<*>} objectStack Object stack. + * @return {Object|undefined} TileMatrix object. + */ +ol.format.WMTSCapabilities.readTileMatrix_ = function(node, objectStack) { + return ol.xml.pushParseAndPop({}, + ol.format.WMTSCapabilities.TM_PARSERS_, node, objectStack); +}; + + +/** + * @const + * @private + * @type {Array.<string>} + */ +ol.format.WMTSCapabilities.NAMESPACE_URIS_ = [ + null, + 'http://www.opengis.net/wmts/1.0' +]; + + +/** + * @const + * @private + * @type {Array.<string>} + */ +ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_ = [ + null, + 'http://www.opengis.net/ows/1.1' +]; + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'Contents': ol.xml.makeObjectPropertySetter( + ol.format.WMTSCapabilities.readContents_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.CONTENTS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'Layer': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readLayer_), + 'TileMatrixSet': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readTileMatrixSet_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.LAYER_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'Style': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readStyle_), + 'Format': ol.xml.makeObjectPropertyPusher( + ol.format.XSD.readString), + 'TileMatrixSetLink': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readTileMatrixSetLink_), + 'Dimension': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readDimensions_), + 'ResourceURL': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readResourceUrl_) + }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { + 'Title': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'Abstract': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'WGS84BoundingBox': ol.xml.makeObjectPropertySetter( + ol.format.WMTSCapabilities.readWgs84BoundingBox_), + 'Identifier': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + })); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.STYLE_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'LegendURL': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readLegendUrl_) + }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { + 'Title': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'Identifier': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + })); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.TMS_LINKS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'TileMatrixSet': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.DIMENSION_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'Default': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'Value': ol.xml.makeObjectPropertyPusher( + ol.format.XSD.readString) + }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { + 'Identifier': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + })); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.WGS84_BBOX_READERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { + 'LowerCorner': ol.xml.makeArrayPusher( + ol.format.WMTSCapabilities.readCoordinates_), + 'UpperCorner': ol.xml.makeArrayPusher( + ol.format.WMTSCapabilities.readCoordinates_) + }); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.TMS_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'WellKnownScaleSet': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'TileMatrix': ol.xml.makeObjectPropertyPusher( + ol.format.WMTSCapabilities.readTileMatrix_) + }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { + 'SupportedCRS': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString), + 'Identifier': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + })); + + +/** + * @const + * @type {Object.<string, Object.<string, ol.XmlParser>>} + * @private + */ +ol.format.WMTSCapabilities.TM_PARSERS_ = ol.xml.makeStructureNS( + ol.format.WMTSCapabilities.NAMESPACE_URIS_, { + 'TopLeftCorner': ol.xml.makeObjectPropertySetter( + ol.format.WMTSCapabilities.readCoordinates_), + 'ScaleDenominator': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readDecimal), + 'TileWidth': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'TileHeight': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'MatrixWidth': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger), + 'MatrixHeight': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readNonNegativeInteger) + }, ol.xml.makeStructureNS(ol.format.WMTSCapabilities.OWS_NAMESPACE_URIS_, { + 'Identifier': ol.xml.makeObjectPropertySetter( + ol.format.XSD.readString) + })); + +// FIXME handle geolocation not supported + +goog.provide('ol.Geolocation'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.Object'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.Polygon'); +goog.require('ol.has'); +goog.require('ol.math'); +goog.require('ol.proj'); +goog.require('ol.sphere.WGS84'); + + +/** + * @enum {string} + */ +ol.GeolocationProperty = { + ACCURACY: 'accuracy', + ACCURACY_GEOMETRY: 'accuracyGeometry', + ALTITUDE: 'altitude', + ALTITUDE_ACCURACY: 'altitudeAccuracy', + HEADING: 'heading', + POSITION: 'position', + PROJECTION: 'projection', + SPEED: 'speed', + TRACKING: 'tracking', + TRACKING_OPTIONS: 'trackingOptions' +}; + + +/** + * @classdesc + * Helper class for providing HTML5 Geolocation capabilities. + * The [Geolocation API](http://www.w3.org/TR/geolocation-API/) + * is used to locate a user's position. + * + * To get notified of position changes, register a listener for the generic + * `change` event on your instance of `ol.Geolocation`. + * + * Example: + * + * var geolocation = new ol.Geolocation({ + * // take the projection to use from the map's view + * projection: view.getProjection() + * }); + * // listen to changes in position + * geolocation.on('change', function(evt) { + * window.console.log(geolocation.getPosition()); + * }); + * + * @fires error + * @constructor + * @extends {ol.Object} + * @param {olx.GeolocationOptions=} opt_options Options. + * @api stable + */ +ol.Geolocation = function(opt_options) { + + ol.Object.call(this); + + var options = opt_options || {}; + + /** + * The unprojected (EPSG:4326) device position. + * @private + * @type {ol.Coordinate} + */ + this.position_ = null; + + /** + * @private + * @type {ol.TransformFunction} + */ + this.transform_ = ol.proj.identityTransform; + + /** + * @private + * @type {number|undefined} + */ + this.watchId_ = undefined; + + ol.events.listen( + this, ol.Object.getChangeEventType(ol.GeolocationProperty.PROJECTION), + this.handleProjectionChanged_, this); + ol.events.listen( + this, ol.Object.getChangeEventType(ol.GeolocationProperty.TRACKING), + this.handleTrackingChanged_, this); + + if (options.projection !== undefined) { + this.setProjection(ol.proj.get(options.projection)); + } + if (options.trackingOptions !== undefined) { + this.setTrackingOptions(options.trackingOptions); + } + + this.setTracking(options.tracking !== undefined ? options.tracking : false); + +}; +ol.inherits(ol.Geolocation, ol.Object); + + +/** + * @inheritDoc + */ +ol.Geolocation.prototype.disposeInternal = function() { + this.setTracking(false); + ol.Object.prototype.disposeInternal.call(this); +}; + + +/** + * @private + */ +ol.Geolocation.prototype.handleProjectionChanged_ = function() { + var projection = this.getProjection(); + if (projection) { + this.transform_ = ol.proj.getTransformFromProjections( + ol.proj.get('EPSG:4326'), projection); + if (this.position_) { + this.set( + ol.GeolocationProperty.POSITION, this.transform_(this.position_)); + } + } +}; + + +/** + * @private + */ +ol.Geolocation.prototype.handleTrackingChanged_ = function() { + if (ol.has.GEOLOCATION) { + var tracking = this.getTracking(); + if (tracking && this.watchId_ === undefined) { + this.watchId_ = ol.global.navigator.geolocation.watchPosition( + this.positionChange_.bind(this), + this.positionError_.bind(this), + this.getTrackingOptions()); + } else if (!tracking && this.watchId_ !== undefined) { + ol.global.navigator.geolocation.clearWatch(this.watchId_); + this.watchId_ = undefined; + } + } +}; + + +/** + * @private + * @param {GeolocationPosition} position position event. + */ +ol.Geolocation.prototype.positionChange_ = function(position) { + var coords = position.coords; + this.set(ol.GeolocationProperty.ACCURACY, coords.accuracy); + this.set(ol.GeolocationProperty.ALTITUDE, + coords.altitude === null ? undefined : coords.altitude); + this.set(ol.GeolocationProperty.ALTITUDE_ACCURACY, + coords.altitudeAccuracy === null ? + undefined : coords.altitudeAccuracy); + this.set(ol.GeolocationProperty.HEADING, coords.heading === null ? + undefined : ol.math.toRadians(coords.heading)); + if (!this.position_) { + this.position_ = [coords.longitude, coords.latitude]; + } else { + this.position_[0] = coords.longitude; + this.position_[1] = coords.latitude; + } + var projectedPosition = this.transform_(this.position_); + this.set(ol.GeolocationProperty.POSITION, projectedPosition); + this.set(ol.GeolocationProperty.SPEED, + coords.speed === null ? undefined : coords.speed); + var geometry = ol.geom.Polygon.circular( + ol.sphere.WGS84, this.position_, coords.accuracy); + geometry.applyTransform(this.transform_); + this.set(ol.GeolocationProperty.ACCURACY_GEOMETRY, geometry); + this.changed(); +}; + +/** + * Triggered when the Geolocation returns an error. + * @event error + * @api + */ + +/** + * @private + * @param {GeolocationPositionError} error error object. + */ +ol.Geolocation.prototype.positionError_ = function(error) { + error.type = ol.events.EventType.ERROR; + this.setTracking(false); + this.dispatchEvent(/** @type {{type: string, target: undefined}} */ (error)); +}; + + +/** + * Get the accuracy of the position in meters. + * @return {number|undefined} The accuracy of the position measurement in + * meters. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getAccuracy = function() { + return /** @type {number|undefined} */ ( + this.get(ol.GeolocationProperty.ACCURACY)); +}; + + +/** + * Get a geometry of the position accuracy. + * @return {?ol.geom.Geometry} A geometry of the position accuracy. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getAccuracyGeometry = function() { + return /** @type {?ol.geom.Geometry} */ ( + this.get(ol.GeolocationProperty.ACCURACY_GEOMETRY) || null); +}; + + +/** + * Get the altitude associated with the position. + * @return {number|undefined} The altitude of the position in meters above mean + * sea level. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getAltitude = function() { + return /** @type {number|undefined} */ ( + this.get(ol.GeolocationProperty.ALTITUDE)); +}; + + +/** + * Get the altitude accuracy of the position. + * @return {number|undefined} The accuracy of the altitude measurement in + * meters. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getAltitudeAccuracy = function() { + return /** @type {number|undefined} */ ( + this.get(ol.GeolocationProperty.ALTITUDE_ACCURACY)); +}; + + +/** + * Get the heading as radians clockwise from North. + * @return {number|undefined} The heading of the device in radians from north. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getHeading = function() { + return /** @type {number|undefined} */ ( + this.get(ol.GeolocationProperty.HEADING)); +}; + + +/** + * Get the position of the device. + * @return {ol.Coordinate|undefined} The current position of the device reported + * in the current projection. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getPosition = function() { + return /** @type {ol.Coordinate|undefined} */ ( + this.get(ol.GeolocationProperty.POSITION)); +}; + + +/** + * Get the projection associated with the position. + * @return {ol.proj.Projection|undefined} The projection the position is + * reported in. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getProjection = function() { + return /** @type {ol.proj.Projection|undefined} */ ( + this.get(ol.GeolocationProperty.PROJECTION)); +}; + + +/** + * Get the speed in meters per second. + * @return {number|undefined} The instantaneous speed of the device in meters + * per second. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getSpeed = function() { + return /** @type {number|undefined} */ ( + this.get(ol.GeolocationProperty.SPEED)); +}; + + +/** + * Determine if the device location is being tracked. + * @return {boolean} The device location is being tracked. + * @observable + * @api stable + */ +ol.Geolocation.prototype.getTracking = function() { + return /** @type {boolean} */ ( + this.get(ol.GeolocationProperty.TRACKING)); +}; + + +/** + * Get the tracking options. + * @see http://www.w3.org/TR/geolocation-API/#position-options + * @return {GeolocationPositionOptions|undefined} PositionOptions as defined by + * the [HTML5 Geolocation spec + * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). + * @observable + * @api stable + */ +ol.Geolocation.prototype.getTrackingOptions = function() { + return /** @type {GeolocationPositionOptions|undefined} */ ( + this.get(ol.GeolocationProperty.TRACKING_OPTIONS)); +}; + + +/** + * Set the projection to use for transforming the coordinates. + * @param {ol.proj.Projection} projection The projection the position is + * reported in. + * @observable + * @api stable + */ +ol.Geolocation.prototype.setProjection = function(projection) { + this.set(ol.GeolocationProperty.PROJECTION, projection); +}; + + +/** + * Enable or disable tracking. + * @param {boolean} tracking Enable tracking. + * @observable + * @api stable + */ +ol.Geolocation.prototype.setTracking = function(tracking) { + this.set(ol.GeolocationProperty.TRACKING, tracking); +}; + + +/** + * Set the tracking options. + * @see http://www.w3.org/TR/geolocation-API/#position-options + * @param {GeolocationPositionOptions} options PositionOptions as defined by the + * [HTML5 Geolocation spec + * ](http://www.w3.org/TR/geolocation-API/#position_options_interface). + * @observable + * @api stable + */ +ol.Geolocation.prototype.setTrackingOptions = function(options) { + this.set(ol.GeolocationProperty.TRACKING_OPTIONS, options); +}; + +goog.provide('ol.geom.Circle'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.geom.flat.deflate'); + + +/** + * @classdesc + * Circle geometry. + * + * @constructor + * @extends {ol.geom.SimpleGeometry} + * @param {ol.Coordinate} center Center. + * @param {number=} opt_radius Radius. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api + */ +ol.geom.Circle = function(center, opt_radius, opt_layout) { + ol.geom.SimpleGeometry.call(this); + var radius = opt_radius ? opt_radius : 0; + this.setCenterAndRadius(center, radius, opt_layout); +}; +ol.inherits(ol.geom.Circle, ol.geom.SimpleGeometry); + + +/** + * Make a complete copy of the geometry. + * @return {!ol.geom.Circle} Clone. + * @api + */ +ol.geom.Circle.prototype.clone = function() { + var circle = new ol.geom.Circle(null); + circle.setFlatCoordinates(this.layout, this.flatCoordinates.slice()); + return circle; +}; + + +/** + * @inheritDoc + */ +ol.geom.Circle.prototype.closestPointXY = function(x, y, closestPoint, minSquaredDistance) { + var flatCoordinates = this.flatCoordinates; + var dx = x - flatCoordinates[0]; + var dy = y - flatCoordinates[1]; + var squaredDistance = dx * dx + dy * dy; + if (squaredDistance < minSquaredDistance) { + var i; + if (squaredDistance === 0) { + for (i = 0; i < this.stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } + } else { + var delta = this.getRadius() / Math.sqrt(squaredDistance); + closestPoint[0] = flatCoordinates[0] + delta * dx; + closestPoint[1] = flatCoordinates[1] + delta * dy; + for (i = 2; i < this.stride; ++i) { + closestPoint[i] = flatCoordinates[i]; + } + } + closestPoint.length = this.stride; + return squaredDistance; + } else { + return minSquaredDistance; + } +}; + + +/** + * @inheritDoc + */ +ol.geom.Circle.prototype.containsXY = function(x, y) { + var flatCoordinates = this.flatCoordinates; + var dx = x - flatCoordinates[0]; + var dy = y - flatCoordinates[1]; + return dx * dx + dy * dy <= this.getRadiusSquared_(); +}; + + +/** + * Return the center of the circle as {@link ol.Coordinate coordinate}. + * @return {ol.Coordinate} Center. + * @api + */ +ol.geom.Circle.prototype.getCenter = function() { + return this.flatCoordinates.slice(0, this.stride); +}; + + +/** + * @inheritDoc + */ +ol.geom.Circle.prototype.computeExtent = function(extent) { + var flatCoordinates = this.flatCoordinates; + var radius = flatCoordinates[this.stride] - flatCoordinates[0]; + return ol.extent.createOrUpdate( + flatCoordinates[0] - radius, flatCoordinates[1] - radius, + flatCoordinates[0] + radius, flatCoordinates[1] + radius, + extent); +}; + + +/** + * Return the radius of the circle. + * @return {number} Radius. + * @api + */ +ol.geom.Circle.prototype.getRadius = function() { + return Math.sqrt(this.getRadiusSquared_()); +}; + + +/** + * @private + * @return {number} Radius squared. + */ +ol.geom.Circle.prototype.getRadiusSquared_ = function() { + var dx = this.flatCoordinates[this.stride] - this.flatCoordinates[0]; + var dy = this.flatCoordinates[this.stride + 1] - this.flatCoordinates[1]; + return dx * dx + dy * dy; +}; + + +/** + * @inheritDoc + * @api + */ +ol.geom.Circle.prototype.getType = function() { + return ol.geom.GeometryType.CIRCLE; +}; + + +/** + * @inheritDoc + * @api stable + */ +ol.geom.Circle.prototype.intersectsExtent = function(extent) { + var circleExtent = this.getExtent(); + if (ol.extent.intersects(extent, circleExtent)) { + var center = this.getCenter(); + + if (extent[0] <= center[0] && extent[2] >= center[0]) { + return true; + } + if (extent[1] <= center[1] && extent[3] >= center[1]) { + return true; + } + + return ol.extent.forEachCorner(extent, this.containsCoordinate, this); + } + return false; + +}; + + +/** + * Set the center of the circle as {@link ol.Coordinate coordinate}. + * @param {ol.Coordinate} center Center. + * @api + */ +ol.geom.Circle.prototype.setCenter = function(center) { + var stride = this.stride; + goog.asserts.assert(center.length == stride, + 'center array length should match stride'); + var radius = this.flatCoordinates[stride] - this.flatCoordinates[0]; + var flatCoordinates = center.slice(); + flatCoordinates[stride] = flatCoordinates[0] + radius; + var i; + for (i = 1; i < stride; ++i) { + flatCoordinates[stride + i] = center[i]; + } + this.setFlatCoordinates(this.layout, flatCoordinates); +}; + + +/** + * Set the center (as {@link ol.Coordinate coordinate}) and the radius (as + * number) of the circle. + * @param {ol.Coordinate} center Center. + * @param {number} radius Radius. + * @param {ol.geom.GeometryLayout=} opt_layout Layout. + * @api + */ +ol.geom.Circle.prototype.setCenterAndRadius = function(center, radius, opt_layout) { + if (!center) { + this.setFlatCoordinates(ol.geom.GeometryLayout.XY, null); + } else { + this.setLayout(opt_layout, center, 0); + if (!this.flatCoordinates) { + this.flatCoordinates = []; + } + /** @type {Array.<number>} */ + var flatCoordinates = this.flatCoordinates; + var offset = ol.geom.flat.deflate.coordinate( + flatCoordinates, 0, center, this.stride); + flatCoordinates[offset++] = flatCoordinates[0] + radius; + var i, ii; + for (i = 1, ii = this.stride; i < ii; ++i) { + flatCoordinates[offset++] = flatCoordinates[i]; + } + flatCoordinates.length = offset; + this.changed(); + } +}; + + +/** + * @param {ol.geom.GeometryLayout} layout Layout. + * @param {Array.<number>} flatCoordinates Flat coordinates. + */ +ol.geom.Circle.prototype.setFlatCoordinates = function(layout, flatCoordinates) { + this.setFlatCoordinatesInternal(layout, flatCoordinates); + this.changed(); +}; + + +/** + * Set the radius of the circle. The radius is in the units of the projection. + * @param {number} radius Radius. + * @api + */ +ol.geom.Circle.prototype.setRadius = function(radius) { + goog.asserts.assert(this.flatCoordinates, + 'truthy this.flatCoordinates expected'); + this.flatCoordinates[this.stride] = this.flatCoordinates[0] + radius; + this.changed(); +}; + + +/** + * Transform each coordinate of the circle from one coordinate reference system + * to another. The geometry is modified in place. + * If you do not want the geometry modified in place, first clone() it and + * then use this function on the clone. + * + * Internally a circle is currently represented by two points: the center of + * the circle `[cx, cy]`, and the point to the right of the circle + * `[cx + r, cy]`. This `transform` function just transforms these two points. + * So the resulting geometry is also a circle, and that circle does not + * correspond to the shape that would be obtained by transforming every point + * of the original circle. + * + * @param {ol.ProjectionLike} source The current projection. Can be a + * string identifier or a {@link ol.proj.Projection} object. + * @param {ol.ProjectionLike} destination The desired projection. Can be a + * string identifier or a {@link ol.proj.Projection} object. + * @return {ol.geom.Circle} This geometry. Note that original geometry is + * modified in place. + * @function + * @api stable + */ +ol.geom.Circle.prototype.transform; + +goog.provide('ol.geom.flat.geodesic'); + +goog.require('goog.asserts'); +goog.require('ol.math'); +goog.require('ol.proj'); + + +/** + * @private + * @param {function(number): ol.Coordinate} interpolate Interpolate function. + * @param {ol.TransformFunction} transform Transform from longitude/latitude to + * projected coordinates. + * @param {number} squaredTolerance Squared tolerance. + * @return {Array.<number>} Flat coordinates. + */ +ol.geom.flat.geodesic.line_ = function(interpolate, transform, squaredTolerance) { + // FIXME reduce garbage generation + // FIXME optimize stack operations + + /** @type {Array.<number>} */ + var flatCoordinates = []; + + var geoA = interpolate(0); + var geoB = interpolate(1); + + var a = transform(geoA); + var b = transform(geoB); + + /** @type {Array.<ol.Coordinate>} */ + var geoStack = [geoB, geoA]; + /** @type {Array.<ol.Coordinate>} */ + var stack = [b, a]; + /** @type {Array.<number>} */ + var fractionStack = [1, 0]; + + /** @type {Object.<string, boolean>} */ + var fractions = {}; + + var maxIterations = 1e5; + var geoM, m, fracA, fracB, fracM, key; + + while (--maxIterations > 0 && fractionStack.length > 0) { + // Pop the a coordinate off the stack + fracA = fractionStack.pop(); + geoA = geoStack.pop(); + a = stack.pop(); + // Add the a coordinate if it has not been added yet + key = fracA.toString(); + if (!(key in fractions)) { + flatCoordinates.push(a[0], a[1]); + fractions[key] = true; + } + // Pop the b coordinate off the stack + fracB = fractionStack.pop(); + geoB = geoStack.pop(); + b = stack.pop(); + // Find the m point between the a and b coordinates + fracM = (fracA + fracB) / 2; + geoM = interpolate(fracM); + m = transform(geoM); + if (ol.math.squaredSegmentDistance(m[0], m[1], a[0], a[1], + b[0], b[1]) < squaredTolerance) { + // If the m point is sufficiently close to the straight line, then we + // discard it. Just use the b coordinate and move on to the next line + // segment. + flatCoordinates.push(b[0], b[1]); + key = fracB.toString(); + goog.asserts.assert(!(key in fractions), + 'fractions object should contain key : ' + key); + fractions[key] = true; + } else { + // Otherwise, we need to subdivide the current line segment. Split it + // into two and push the two line segments onto the stack. + fractionStack.push(fracB, fracM, fracM, fracA); + stack.push(b, m, m, a); + geoStack.push(geoB, geoM, geoM, geoA); + } + } + goog.asserts.assert(maxIterations > 0, + 'maxIterations should be more than 0'); + + return flatCoordinates; +}; + + +/** +* Generate a great-circle arcs between two lat/lon points. +* @param {number} lon1 Longitude 1 in degrees. +* @param {number} lat1 Latitude 1 in degrees. +* @param {number} lon2 Longitude 2 in degrees. +* @param {number} lat2 Latitude 2 in degrees. + * @param {ol.proj.Projection} projection Projection. +* @param {number} squaredTolerance Squared tolerance. +* @return {Array.<number>} Flat coordinates. +*/ +ol.geom.flat.geodesic.greatCircleArc = function( + lon1, lat1, lon2, lat2, projection, squaredTolerance) { + + var geoProjection = ol.proj.get('EPSG:4326'); + + var cosLat1 = Math.cos(ol.math.toRadians(lat1)); + var sinLat1 = Math.sin(ol.math.toRadians(lat1)); + var cosLat2 = Math.cos(ol.math.toRadians(lat2)); + var sinLat2 = Math.sin(ol.math.toRadians(lat2)); + var cosDeltaLon = Math.cos(ol.math.toRadians(lon2 - lon1)); + var sinDeltaLon = Math.sin(ol.math.toRadians(lon2 - lon1)); + var d = sinLat1 * sinLat2 + cosLat1 * cosLat2 * cosDeltaLon; + + return ol.geom.flat.geodesic.line_( + /** + * @param {number} frac Fraction. + * @return {ol.Coordinate} Coordinate. + */ + function(frac) { + if (1 <= d) { + return [lon2, lat2]; + } + var D = frac * Math.acos(d); + var cosD = Math.cos(D); + var sinD = Math.sin(D); + var y = sinDeltaLon * cosLat2; + var x = cosLat1 * sinLat2 - sinLat1 * cosLat2 * cosDeltaLon; + var theta = Math.atan2(y, x); + var lat = Math.asin(sinLat1 * cosD + cosLat1 * sinD * Math.cos(theta)); + var lon = ol.math.toRadians(lon1) + + Math.atan2(Math.sin(theta) * sinD * cosLat1, + cosD - sinLat1 * Math.sin(lat)); + return [ol.math.toDegrees(lon), ol.math.toDegrees(lat)]; + }, ol.proj.getTransform(geoProjection, projection), squaredTolerance); +}; + + +/** + * Generate a meridian (line at constant longitude). + * @param {number} lon Longitude. + * @param {number} lat1 Latitude 1. + * @param {number} lat2 Latitude 2. + * @param {ol.proj.Projection} projection Projection. + * @param {number} squaredTolerance Squared tolerance. + * @return {Array.<number>} Flat coordinates. + */ +ol.geom.flat.geodesic.meridian = function(lon, lat1, lat2, projection, squaredTolerance) { + var epsg4326Projection = ol.proj.get('EPSG:4326'); + return ol.geom.flat.geodesic.line_( + /** + * @param {number} frac Fraction. + * @return {ol.Coordinate} Coordinate. + */ + function(frac) { + return [lon, lat1 + ((lat2 - lat1) * frac)]; + }, + ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance); +}; + + +/** + * Generate a parallel (line at constant latitude). + * @param {number} lat Latitude. + * @param {number} lon1 Longitude 1. + * @param {number} lon2 Longitude 2. + * @param {ol.proj.Projection} projection Projection. + * @param {number} squaredTolerance Squared tolerance. + * @return {Array.<number>} Flat coordinates. + */ +ol.geom.flat.geodesic.parallel = function(lat, lon1, lon2, projection, squaredTolerance) { + var epsg4326Projection = ol.proj.get('EPSG:4326'); + return ol.geom.flat.geodesic.line_( + /** + * @param {number} frac Fraction. + * @return {ol.Coordinate} Coordinate. + */ + function(frac) { + return [lon1 + ((lon2 - lon1) * frac), lat]; + }, + ol.proj.getTransform(epsg4326Projection, projection), squaredTolerance); +}; + +goog.provide('ol.Graticule'); + +goog.require('goog.asserts'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.flat.geodesic'); +goog.require('ol.math'); +goog.require('ol.proj'); +goog.require('ol.render.EventType'); +goog.require('ol.style.Stroke'); + + +/** + * Render a grid for a coordinate system on a map. + * @constructor + * @param {olx.GraticuleOptions=} opt_options Options. + * @api + */ +ol.Graticule = function(opt_options) { + + var options = opt_options || {}; + + /** + * @type {ol.Map} + * @private + */ + this.map_ = null; + + /** + * @type {ol.proj.Projection} + * @private + */ + this.projection_ = null; + + /** + * @type {number} + * @private + */ + this.maxLat_ = Infinity; + + /** + * @type {number} + * @private + */ + this.maxLon_ = Infinity; + + /** + * @type {number} + * @private + */ + this.minLat_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.minLon_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.maxLatP_ = Infinity; + + /** + * @type {number} + * @private + */ + this.maxLonP_ = Infinity; + + /** + * @type {number} + * @private + */ + this.minLatP_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.minLonP_ = -Infinity; + + /** + * @type {number} + * @private + */ + this.targetSize_ = options.targetSize !== undefined ? + options.targetSize : 100; + + /** + * @type {number} + * @private + */ + this.maxLines_ = options.maxLines !== undefined ? options.maxLines : 100; + goog.asserts.assert(this.maxLines_ > 0, + 'this.maxLines_ should be more than 0'); + + /** + * @type {Array.<ol.geom.LineString>} + * @private + */ + this.meridians_ = []; + + /** + * @type {Array.<ol.geom.LineString>} + * @private + */ + this.parallels_ = []; + + /** + * @type {ol.style.Stroke} + * @private + */ + this.strokeStyle_ = options.strokeStyle !== undefined ? + options.strokeStyle : ol.Graticule.DEFAULT_STROKE_STYLE_; + + /** + * @type {ol.TransformFunction|undefined} + * @private + */ + this.fromLonLatTransform_ = undefined; + + /** + * @type {ol.TransformFunction|undefined} + * @private + */ + this.toLonLatTransform_ = undefined; + + /** + * @type {ol.Coordinate} + * @private + */ + this.projectionCenterLonLat_ = null; + + this.setMap(options.map !== undefined ? options.map : null); +}; + + +/** + * @type {ol.style.Stroke} + * @private + * @const + */ +ol.Graticule.DEFAULT_STROKE_STYLE_ = new ol.style.Stroke({ + color: 'rgba(0,0,0,0.2)' +}); + + +/** + * TODO can be configurable + * @type {Array.<number>} + * @private + */ +ol.Graticule.intervals_ = [90, 45, 30, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, + 0.01, 0.005, 0.002, 0.001]; + + +/** + * @param {number} lon Longitude. + * @param {number} minLat Minimal latitude. + * @param {number} maxLat Maximal latitude. + * @param {number} squaredTolerance Squared tolerance. + * @param {ol.Extent} extent Extent. + * @param {number} index Index. + * @return {number} Index. + * @private + */ +ol.Graticule.prototype.addMeridian_ = function(lon, minLat, maxLat, squaredTolerance, extent, index) { + var lineString = this.getMeridian_(lon, minLat, maxLat, + squaredTolerance, index); + if (ol.extent.intersects(lineString.getExtent(), extent)) { + this.meridians_[index++] = lineString; + } + return index; +}; + + +/** + * @param {number} lat Latitude. + * @param {number} minLon Minimal longitude. + * @param {number} maxLon Maximal longitude. + * @param {number} squaredTolerance Squared tolerance. + * @param {ol.Extent} extent Extent. + * @param {number} index Index. + * @return {number} Index. + * @private + */ +ol.Graticule.prototype.addParallel_ = function(lat, minLon, maxLon, squaredTolerance, extent, index) { + var lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, + index); + if (ol.extent.intersects(lineString.getExtent(), extent)) { + this.parallels_[index++] = lineString; + } + return index; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.Coordinate} center Center. + * @param {number} resolution Resolution. + * @param {number} squaredTolerance Squared tolerance. + * @private + */ +ol.Graticule.prototype.createGraticule_ = function(extent, center, resolution, squaredTolerance) { + + var interval = this.getInterval_(resolution); + if (interval == -1) { + this.meridians_.length = this.parallels_.length = 0; + return; + } + + var centerLonLat = this.toLonLatTransform_(center); + var centerLon = centerLonLat[0]; + var centerLat = centerLonLat[1]; + var maxLines = this.maxLines_; + var cnt, idx, lat, lon; + + var validExtent = [ + Math.max(extent[0], this.minLonP_), + Math.max(extent[1], this.minLatP_), + Math.min(extent[2], this.maxLonP_), + Math.min(extent[3], this.maxLatP_) + ]; + + validExtent = ol.proj.transformExtent(validExtent, this.projection_, + 'EPSG:4326'); + var maxLat = validExtent[3]; + var maxLon = validExtent[2]; + var minLat = validExtent[1]; + var minLon = validExtent[0]; + + // Create meridians + + centerLon = Math.floor(centerLon / interval) * interval; + lon = ol.math.clamp(centerLon, this.minLon_, this.maxLon_); + + idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0); + + cnt = 0; + while (lon != this.minLon_ && cnt++ < maxLines) { + lon = Math.max(lon - interval, this.minLon_); + idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); + } + + lon = ol.math.clamp(centerLon, this.minLon_, this.maxLon_); + + cnt = 0; + while (lon != this.maxLon_ && cnt++ < maxLines) { + lon = Math.min(lon + interval, this.maxLon_); + idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx); + } + + this.meridians_.length = idx; + + // Create parallels + + centerLat = Math.floor(centerLat / interval) * interval; + lat = ol.math.clamp(centerLat, this.minLat_, this.maxLat_); + + idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, 0); + + cnt = 0; + while (lat != this.minLat_ && cnt++ < maxLines) { + lat = Math.max(lat - interval, this.minLat_); + idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); + } + + lat = ol.math.clamp(centerLat, this.minLat_, this.maxLat_); + + cnt = 0; + while (lat != this.maxLat_ && cnt++ < maxLines) { + lat = Math.min(lat + interval, this.maxLat_); + idx = this.addParallel_(lat, minLon, maxLon, squaredTolerance, extent, idx); + } + + this.parallels_.length = idx; + +}; + + +/** + * @param {number} resolution Resolution. + * @return {number} The interval in degrees. + * @private + */ +ol.Graticule.prototype.getInterval_ = function(resolution) { + var centerLon = this.projectionCenterLonLat_[0]; + var centerLat = this.projectionCenterLonLat_[1]; + var interval = -1; + var i, ii, delta, dist; + var target = Math.pow(this.targetSize_ * resolution, 2); + /** @type {Array.<number>} **/ + var p1 = []; + /** @type {Array.<number>} **/ + var p2 = []; + for (i = 0, ii = ol.Graticule.intervals_.length; i < ii; ++i) { + delta = ol.Graticule.intervals_[i] / 2; + p1[0] = centerLon - delta; + p1[1] = centerLat - delta; + p2[0] = centerLon + delta; + p2[1] = centerLat + delta; + this.fromLonLatTransform_(p1, p1); + this.fromLonLatTransform_(p2, p2); + dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2); + if (dist <= target) { + break; + } + interval = ol.Graticule.intervals_[i]; + } + return interval; +}; + + +/** + * Get the map associated with this graticule. + * @return {ol.Map} The map. + * @api + */ +ol.Graticule.prototype.getMap = function() { + return this.map_; +}; + + +/** + * @param {number} lon Longitude. + * @param {number} minLat Minimal latitude. + * @param {number} maxLat Maximal latitude. + * @param {number} squaredTolerance Squared tolerance. + * @return {ol.geom.LineString} The meridian line string. + * @param {number} index Index. + * @private + */ +ol.Graticule.prototype.getMeridian_ = function(lon, minLat, maxLat, + squaredTolerance, index) { + goog.asserts.assert(lon >= this.minLon_, + 'lon should be larger than or equal to this.minLon_'); + goog.asserts.assert(lon <= this.maxLon_, + 'lon should be smaller than or equal to this.maxLon_'); + var flatCoordinates = ol.geom.flat.geodesic.meridian(lon, + minLat, maxLat, this.projection_, squaredTolerance); + goog.asserts.assert(flatCoordinates.length > 0, + 'flatCoordinates cannot be empty'); + var lineString = this.meridians_[index] !== undefined ? + this.meridians_[index] : new ol.geom.LineString(null); + lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates); + return lineString; +}; + + +/** + * Get the list of meridians. Meridians are lines of equal longitude. + * @return {Array.<ol.geom.LineString>} The meridians. + * @api + */ +ol.Graticule.prototype.getMeridians = function() { + return this.meridians_; +}; + + +/** + * @param {number} lat Latitude. + * @param {number} minLon Minimal longitude. + * @param {number} maxLon Maximal longitude. + * @param {number} squaredTolerance Squared tolerance. + * @return {ol.geom.LineString} The parallel line string. + * @param {number} index Index. + * @private + */ +ol.Graticule.prototype.getParallel_ = function(lat, minLon, maxLon, + squaredTolerance, index) { + goog.asserts.assert(lat >= this.minLat_, + 'lat should be larger than or equal to this.minLat_'); + goog.asserts.assert(lat <= this.maxLat_, + 'lat should be smaller than or equal to this.maxLat_'); + var flatCoordinates = ol.geom.flat.geodesic.parallel(lat, + this.minLon_, this.maxLon_, this.projection_, squaredTolerance); + goog.asserts.assert(flatCoordinates.length > 0, + 'flatCoordinates cannot be empty'); + var lineString = this.parallels_[index] !== undefined ? + this.parallels_[index] : new ol.geom.LineString(null); + lineString.setFlatCoordinates(ol.geom.GeometryLayout.XY, flatCoordinates); + return lineString; +}; + + +/** + * Get the list of parallels. Pallels are lines of equal latitude. + * @return {Array.<ol.geom.LineString>} The parallels. + * @api + */ +ol.Graticule.prototype.getParallels = function() { + return this.parallels_; +}; + + +/** + * @param {ol.render.Event} e Event. + * @private + */ +ol.Graticule.prototype.handlePostCompose_ = function(e) { + var vectorContext = e.vectorContext; + var frameState = e.frameState; + var extent = frameState.extent; + var viewState = frameState.viewState; + var center = viewState.center; + var projection = viewState.projection; + var resolution = viewState.resolution; + var pixelRatio = frameState.pixelRatio; + var squaredTolerance = + resolution * resolution / (4 * pixelRatio * pixelRatio); + + var updateProjectionInfo = !this.projection_ || + !ol.proj.equivalent(this.projection_, projection); + + if (updateProjectionInfo) { + this.updateProjectionInfo_(projection); + } + + //Fix the extent if wrapped. + //(note: this is the same extent as vectorContext.extent_) + var offsetX = 0; + if (projection.canWrapX()) { + var projectionExtent = projection.getExtent(); + var worldWidth = ol.extent.getWidth(projectionExtent); + var x = frameState.focus[0]; + if (x < projectionExtent[0] || x > projectionExtent[2]) { + var worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth); + offsetX = worldWidth * worldsAway; + extent = [ + extent[0] + offsetX, extent[1], + extent[2] + offsetX, extent[3] + ]; + } + } + + this.createGraticule_(extent, center, resolution, squaredTolerance); + + // Draw the lines + vectorContext.setFillStrokeStyle(null, this.strokeStyle_); + var i, l, line; + for (i = 0, l = this.meridians_.length; i < l; ++i) { + line = this.meridians_[i]; + vectorContext.drawLineString(line, null); + } + for (i = 0, l = this.parallels_.length; i < l; ++i) { + line = this.parallels_[i]; + vectorContext.drawLineString(line, null); + } +}; + + +/** + * @param {ol.proj.Projection} projection Projection. + * @private + */ +ol.Graticule.prototype.updateProjectionInfo_ = function(projection) { + goog.asserts.assert(projection, 'projection cannot be null'); + + var epsg4326Projection = ol.proj.get('EPSG:4326'); + + var extent = projection.getExtent(); + var worldExtent = projection.getWorldExtent(); + var worldExtentP = ol.proj.transformExtent(worldExtent, + epsg4326Projection, projection); + + var maxLat = worldExtent[3]; + var maxLon = worldExtent[2]; + var minLat = worldExtent[1]; + var minLon = worldExtent[0]; + + var maxLatP = worldExtentP[3]; + var maxLonP = worldExtentP[2]; + var minLatP = worldExtentP[1]; + var minLonP = worldExtentP[0]; + + goog.asserts.assert(extent, 'extent cannot be null'); + goog.asserts.assert(maxLat !== undefined, 'maxLat should be defined'); + goog.asserts.assert(maxLon !== undefined, 'maxLon should be defined'); + goog.asserts.assert(minLat !== undefined, 'minLat should be defined'); + goog.asserts.assert(minLon !== undefined, 'minLon should be defined'); + + goog.asserts.assert(maxLatP !== undefined, + 'projected maxLat should be defined'); + goog.asserts.assert(maxLonP !== undefined, + 'projected maxLon should be defined'); + goog.asserts.assert(minLatP !== undefined, + 'projected minLat should be defined'); + goog.asserts.assert(minLonP !== undefined, + 'projected minLon should be defined'); + + this.maxLat_ = maxLat; + this.maxLon_ = maxLon; + this.minLat_ = minLat; + this.minLon_ = minLon; + + this.maxLatP_ = maxLatP; + this.maxLonP_ = maxLonP; + this.minLatP_ = minLatP; + this.minLonP_ = minLonP; + + + this.fromLonLatTransform_ = ol.proj.getTransform( + epsg4326Projection, projection); + + this.toLonLatTransform_ = ol.proj.getTransform( + projection, epsg4326Projection); + + this.projectionCenterLonLat_ = this.toLonLatTransform_( + ol.extent.getCenter(extent)); + + this.projection_ = projection; +}; + + +/** + * Set the map for this graticule. The graticule will be rendered on the + * provided map. + * @param {ol.Map} map Map. + * @api + */ +ol.Graticule.prototype.setMap = function(map) { + if (this.map_) { + this.map_.un(ol.render.EventType.POSTCOMPOSE, + this.handlePostCompose_, this); + this.map_.render(); + } + if (map) { + map.on(ol.render.EventType.POSTCOMPOSE, + this.handlePostCompose_, this); + map.render(); + } + this.map_ = map; +}; + +goog.provide('ol.Image'); + +goog.require('goog.asserts'); +goog.require('ol.ImageBase'); +goog.require('ol.ImageState'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.object'); + + +/** + * @constructor + * @extends {ol.ImageBase} + * @param {ol.Extent} extent Extent. + * @param {number|undefined} resolution Resolution. + * @param {number} pixelRatio Pixel ratio. + * @param {Array.<ol.Attribution>} attributions Attributions. + * @param {string} src Image source URI. + * @param {?string} crossOrigin Cross origin. + * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. + */ +ol.Image = function(extent, resolution, pixelRatio, attributions, src, + crossOrigin, imageLoadFunction) { + + ol.ImageBase.call(this, extent, resolution, pixelRatio, ol.ImageState.IDLE, + attributions); + + /** + * @private + * @type {string} + */ + this.src_ = src; + + /** + * @private + * @type {HTMLCanvasElement|Image|HTMLVideoElement} + */ + this.image_ = new Image(); + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {Object.<number, (HTMLCanvasElement|Image|HTMLVideoElement)>} + */ + this.imageByContext_ = {}; + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.imageListenerKeys_ = null; + + /** + * @protected + * @type {ol.ImageState} + */ + this.state = ol.ImageState.IDLE; + + /** + * @private + * @type {ol.ImageLoadFunctionType} + */ + this.imageLoadFunction_ = imageLoadFunction; + +}; +ol.inherits(ol.Image, ol.ImageBase); + + +/** + * Get the HTML image element (may be a Canvas, Image, or Video). + * @param {Object=} opt_context Object. + * @return {HTMLCanvasElement|Image|HTMLVideoElement} Image. + * @api + */ +ol.Image.prototype.getImage = function(opt_context) { + if (opt_context !== undefined) { + var image; + var key = goog.getUid(opt_context); + if (key in this.imageByContext_) { + return this.imageByContext_[key]; + } else if (ol.object.isEmpty(this.imageByContext_)) { + image = this.image_; + } else { + image = /** @type {Image} */ (this.image_.cloneNode(false)); + } + this.imageByContext_[key] = image; + return image; + } else { + return this.image_; + } +}; + + +/** + * Tracks loading or read errors. + * + * @private + */ +ol.Image.prototype.handleImageError_ = function() { + this.state = ol.ImageState.ERROR; + this.unlistenImage_(); + this.changed(); +}; + + +/** + * Tracks successful image load. + * + * @private + */ +ol.Image.prototype.handleImageLoad_ = function() { + if (this.resolution === undefined) { + this.resolution = ol.extent.getHeight(this.extent) / this.image_.height; + } + this.state = ol.ImageState.LOADED; + this.unlistenImage_(); + this.changed(); +}; + + +/** + * Load the image or retry if loading previously failed. + * Loading is taken care of by the tile queue, and calling this method is + * only needed for preloading or for reloading in case of an error. + * @api + */ +ol.Image.prototype.load = function() { + if (this.state == ol.ImageState.IDLE || this.state == ol.ImageState.ERROR) { + this.state = ol.ImageState.LOADING; + this.changed(); + goog.asserts.assert(!this.imageListenerKeys_, + 'this.imageListenerKeys_ should be null'); + this.imageListenerKeys_ = [ + ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, + this.handleImageError_, this), + ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, + this.handleImageLoad_, this) + ]; + this.imageLoadFunction_(this, this.src_); + } +}; + + +/** + * @param {HTMLCanvasElement|Image|HTMLVideoElement} image Image. + */ +ol.Image.prototype.setImage = function(image) { + this.image_ = image; +}; + + +/** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ +ol.Image.prototype.unlistenImage_ = function() { + goog.asserts.assert(this.imageListenerKeys_, + 'this.imageListenerKeys_ should not be null'); + this.imageListenerKeys_.forEach(ol.events.unlistenByKey); + this.imageListenerKeys_ = null; +}; + +goog.provide('ol.ImageTile'); + +goog.require('goog.asserts'); +goog.require('ol.Tile'); +goog.require('ol.TileState'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.object'); + + +/** + * @constructor + * @extends {ol.Tile} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.TileState} state State. + * @param {string} src Image source URI. + * @param {?string} crossOrigin Cross origin. + * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. + */ +ol.ImageTile = function(tileCoord, state, src, crossOrigin, tileLoadFunction) { + + ol.Tile.call(this, tileCoord, state); + + /** + * Image URI + * + * @private + * @type {string} + */ + this.src_ = src; + + /** + * @private + * @type {Image} + */ + this.image_ = new Image(); + if (crossOrigin !== null) { + this.image_.crossOrigin = crossOrigin; + } + + /** + * @private + * @type {Object.<number, Image>} + */ + this.imageByContext_ = {}; + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.imageListenerKeys_ = null; + + /** + * @private + * @type {ol.TileLoadFunctionType} + */ + this.tileLoadFunction_ = tileLoadFunction; + +}; +ol.inherits(ol.ImageTile, ol.Tile); + + +/** + * @inheritDoc + */ +ol.ImageTile.prototype.disposeInternal = function() { + if (this.state == ol.TileState.LOADING) { + this.unlistenImage_(); + } + if (this.interimTile) { + this.interimTile.dispose(); + } + this.state = ol.TileState.ABORT; + this.changed(); + ol.Tile.prototype.disposeInternal.call(this); +}; + + +/** + * Get the image element for this tile. + * @inheritDoc + * @api + */ +ol.ImageTile.prototype.getImage = function(opt_context) { + if (opt_context !== undefined) { + var image; + var key = goog.getUid(opt_context); + if (key in this.imageByContext_) { + return this.imageByContext_[key]; + } else if (ol.object.isEmpty(this.imageByContext_)) { + image = this.image_; + } else { + image = /** @type {Image} */ (this.image_.cloneNode(false)); + } + this.imageByContext_[key] = image; + return image; + } else { + return this.image_; + } +}; + + +/** + * @inheritDoc + */ +ol.ImageTile.prototype.getKey = function() { + return this.src_; +}; + + +/** + * Tracks loading or read errors. + * + * @private + */ +ol.ImageTile.prototype.handleImageError_ = function() { + this.state = ol.TileState.ERROR; + this.unlistenImage_(); + this.changed(); +}; + + +/** + * Tracks successful image load. + * + * @private + */ +ol.ImageTile.prototype.handleImageLoad_ = function() { + if (this.image_.naturalWidth && this.image_.naturalHeight) { + this.state = ol.TileState.LOADED; + } else { + this.state = ol.TileState.EMPTY; + } + this.unlistenImage_(); + this.changed(); +}; + + +/** + * Load the image or retry if loading previously failed. + * Loading is taken care of by the tile queue, and calling this method is + * only needed for preloading or for reloading in case of an error. + * @api + */ +ol.ImageTile.prototype.load = function() { + if (this.state == ol.TileState.IDLE || this.state == ol.TileState.ERROR) { + this.state = ol.TileState.LOADING; + this.changed(); + goog.asserts.assert(!this.imageListenerKeys_, + 'this.imageListenerKeys_ should be null'); + this.imageListenerKeys_ = [ + ol.events.listenOnce(this.image_, ol.events.EventType.ERROR, + this.handleImageError_, this), + ol.events.listenOnce(this.image_, ol.events.EventType.LOAD, + this.handleImageLoad_, this) + ]; + this.tileLoadFunction_(this, this.src_); + } +}; + + +/** + * Discards event handlers which listen for load completion or errors. + * + * @private + */ +ol.ImageTile.prototype.unlistenImage_ = function() { + goog.asserts.assert(this.imageListenerKeys_, + 'this.imageListenerKeys_ should not be null'); + this.imageListenerKeys_.forEach(ol.events.unlistenByKey); + this.imageListenerKeys_ = null; +}; + +// FIXME should handle all geo-referenced data, not just vector data + +goog.provide('ol.interaction.DragAndDrop'); +goog.provide('ol.interaction.DragAndDropEvent'); + +goog.require('goog.asserts'); +goog.require('ol.functions'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.EventType'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.proj'); + + +/** + * @classdesc + * Handles input of vector data by drag and drop. + * + * @constructor + * @extends {ol.interaction.Interaction} + * @fires ol.interaction.DragAndDropEvent + * @param {olx.interaction.DragAndDropOptions=} opt_options Options. + * @api stable + */ +ol.interaction.DragAndDrop = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.interaction.Interaction.call(this, { + handleEvent: ol.interaction.DragAndDrop.handleEvent + }); + + /** + * @private + * @type {Array.<function(new: ol.format.Feature)>} + */ + this.formatConstructors_ = options.formatConstructors ? + options.formatConstructors : []; + + /** + * @private + * @type {ol.proj.Projection} + */ + this.projection_ = options.projection ? + ol.proj.get(options.projection) : null; + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.dropListenKeys_ = null; + + /** + * @private + * @type {Element} + */ + this.target = options.target ? options.target : null; + +}; +ol.inherits(ol.interaction.DragAndDrop, ol.interaction.Interaction); + + +/** + * @param {Event} event Event. + * @this {ol.interaction.DragAndDrop} + * @private + */ +ol.interaction.DragAndDrop.handleDrop_ = function(event) { + var files = event.dataTransfer.files; + var i, ii, file; + for (i = 0, ii = files.length; i < ii; ++i) { + file = files.item(i); + var reader = new FileReader(); + reader.addEventListener(ol.events.EventType.LOAD, + this.handleResult_.bind(this, file)); + reader.readAsText(file); + } +}; + + +/** + * @param {Event} event Event. + * @private + */ +ol.interaction.DragAndDrop.handleStop_ = function(event) { + event.stopPropagation(); + event.preventDefault(); + event.dataTransfer.dropEffect = 'copy'; +}; + + +/** + * @param {File} file File. + * @param {Event} event Load event. + * @private + */ +ol.interaction.DragAndDrop.prototype.handleResult_ = function(file, event) { + var result = event.target.result; + var map = this.getMap(); + goog.asserts.assert(map, 'map must be set'); + var projection = this.projection_; + if (!projection) { + var view = map.getView(); + goog.asserts.assert(view, 'map must have view'); + projection = view.getProjection(); + goog.asserts.assert(projection !== undefined, + 'projection should be defined'); + } + var formatConstructors = this.formatConstructors_; + var features = []; + var i, ii; + for (i = 0, ii = formatConstructors.length; i < ii; ++i) { + var formatConstructor = formatConstructors[i]; + var format = new formatConstructor(); + features = this.tryReadFeatures_(format, result, { + featureProjection: projection + }); + if (features && features.length > 0) { + break; + } + } + this.dispatchEvent( + new ol.interaction.DragAndDropEvent( + ol.interaction.DragAndDropEventType.ADD_FEATURES, this, file, + features, projection)); +}; + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} unconditionally and + * neither prevents the browser default nor stops event propagation. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.DragAndDrop} + * @api + */ +ol.interaction.DragAndDrop.handleEvent = ol.functions.TRUE; + + +/** + * @inheritDoc + */ +ol.interaction.DragAndDrop.prototype.setMap = function(map) { + if (this.dropListenKeys_) { + this.dropListenKeys_.forEach(ol.events.unlistenByKey); + this.dropListenKeys_ = null; + } + ol.interaction.Interaction.prototype.setMap.call(this, map); + if (map) { + var dropArea = this.target ? this.target : map.getViewport(); + this.dropListenKeys_ = [ + ol.events.listen(dropArea, ol.events.EventType.DROP, + ol.interaction.DragAndDrop.handleDrop_, this), + ol.events.listen(dropArea, ol.events.EventType.DRAGENTER, + ol.interaction.DragAndDrop.handleStop_, this), + ol.events.listen(dropArea, ol.events.EventType.DRAGOVER, + ol.interaction.DragAndDrop.handleStop_, this), + ol.events.listen(dropArea, ol.events.EventType.DROP, + ol.interaction.DragAndDrop.handleStop_, this) + ]; + } +}; + + +/** + * @param {ol.format.Feature} format Format. + * @param {string} text Text. + * @param {olx.format.ReadOptions} options Read options. + * @private + * @return {Array.<ol.Feature>} Features. + */ +ol.interaction.DragAndDrop.prototype.tryReadFeatures_ = function(format, text, options) { + try { + return format.readFeatures(text, options); + } catch (e) { + return null; + } +}; + + +/** + * @enum {string} + */ +ol.interaction.DragAndDropEventType = { + /** + * Triggered when features are added + * @event ol.interaction.DragAndDropEvent#addfeatures + * @api stable + */ + ADD_FEATURES: 'addfeatures' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.interaction.DragAndDrop} instances are instances + * of this type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.interaction.DragAndDropEvent} + * @param {ol.interaction.DragAndDropEventType} type Type. + * @param {Object} target Target. + * @param {File} file File. + * @param {Array.<ol.Feature>=} opt_features Features. + * @param {ol.proj.Projection=} opt_projection Projection. + */ +ol.interaction.DragAndDropEvent = function(type, target, file, opt_features, opt_projection) { + + ol.events.Event.call(this, type, target); + + /** + * The features parsed from dropped data. + * @type {Array.<ol.Feature>|undefined} + * @api stable + */ + this.features = opt_features; + + /** + * The dropped file. + * @type {File} + * @api stable + */ + this.file = file; + + /** + * The feature projection. + * @type {ol.proj.Projection|undefined} + * @api + */ + this.projection = opt_projection; + +}; +ol.inherits(ol.interaction.DragAndDropEvent, ol.events.Event); + +goog.provide('ol.interaction.DragRotateAndZoom'); + +goog.require('ol'); +goog.require('ol.ViewHint'); +goog.require('ol.events.condition'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.interaction.Pointer'); + + +/** + * @classdesc + * Allows the user to zoom and rotate the map by clicking and dragging + * on the map. By default, this interaction is limited to when the shift + * key is held down. + * + * This interaction is only supported for mouse devices. + * + * And this interaction is not included in the default interactions. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.DragRotateAndZoomOptions=} opt_options Options. + * @api stable + */ +ol.interaction.DragRotateAndZoom = function(opt_options) { + + var options = opt_options ? opt_options : {}; + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.DragRotateAndZoom.handleDownEvent_, + handleDragEvent: ol.interaction.DragRotateAndZoom.handleDragEvent_, + handleUpEvent: ol.interaction.DragRotateAndZoom.handleUpEvent_ + }); + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? + options.condition : ol.events.condition.shiftKeyOnly; + + /** + * @private + * @type {number|undefined} + */ + this.lastAngle_ = undefined; + + /** + * @private + * @type {number|undefined} + */ + this.lastMagnitude_ = undefined; + + /** + * @private + * @type {number} + */ + this.lastScaleDelta_ = 0; + + /** + * @private + * @type {number} + */ + this.duration_ = options.duration !== undefined ? options.duration : 400; + +}; +ol.inherits(ol.interaction.DragRotateAndZoom, ol.interaction.Pointer); + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @this {ol.interaction.DragRotateAndZoom} + * @private + */ +ol.interaction.DragRotateAndZoom.handleDragEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return; + } + + var map = mapBrowserEvent.map; + var size = map.getSize(); + var offset = mapBrowserEvent.pixel; + var deltaX = offset[0] - size[0] / 2; + var deltaY = size[1] / 2 - offset[1]; + var theta = Math.atan2(deltaY, deltaX); + var magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY); + var view = map.getView(); + map.render(); + if (this.lastAngle_ !== undefined) { + var angleDelta = theta - this.lastAngle_; + ol.interaction.Interaction.rotateWithoutConstraints( + map, view, view.getRotation() - angleDelta); + } + this.lastAngle_ = theta; + if (this.lastMagnitude_ !== undefined) { + var resolution = this.lastMagnitude_ * (view.getResolution() / magnitude); + ol.interaction.Interaction.zoomWithoutConstraints(map, view, resolution); + } + if (this.lastMagnitude_ !== undefined) { + this.lastScaleDelta_ = this.lastMagnitude_ / magnitude; + } + this.lastMagnitude_ = magnitude; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.DragRotateAndZoom} + * @private + */ +ol.interaction.DragRotateAndZoom.handleUpEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return true; + } + + var map = mapBrowserEvent.map; + var view = map.getView(); + view.setHint(ol.ViewHint.INTERACTING, -1); + var direction = this.lastScaleDelta_ - 1; + ol.interaction.Interaction.rotate(map, view, view.getRotation()); + ol.interaction.Interaction.zoom(map, view, view.getResolution(), + undefined, this.duration_, direction); + this.lastScaleDelta_ = 0; + return false; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} mapBrowserEvent Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.DragRotateAndZoom} + * @private + */ +ol.interaction.DragRotateAndZoom.handleDownEvent_ = function(mapBrowserEvent) { + if (!ol.events.condition.mouseOnly(mapBrowserEvent)) { + return false; + } + + if (this.condition_(mapBrowserEvent)) { + mapBrowserEvent.map.getView().setHint(ol.ViewHint.INTERACTING, 1); + this.lastAngle_ = undefined; + this.lastMagnitude_ = undefined; + return true; + } else { + return false; + } +}; + +goog.provide('ol.interaction.Draw'); +goog.provide('ol.interaction.DrawEvent'); +goog.provide('ol.interaction.DrawEventType'); +goog.provide('ol.interaction.DrawMode'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.Collection'); +goog.require('ol.Feature'); +goog.require('ol.MapBrowserEvent'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.Object'); +goog.require('ol.coordinate'); +goog.require('ol.functions'); +goog.require('ol.events.condition'); +goog.require('ol.geom.Circle'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.interaction.InteractionProperty'); +goog.require('ol.interaction.Pointer'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); + + +/** + * @enum {string} + */ +ol.interaction.DrawEventType = { + /** + * Triggered upon feature draw start + * @event ol.interaction.DrawEvent#drawstart + * @api stable + */ + DRAWSTART: 'drawstart', + /** + * Triggered upon feature draw end + * @event ol.interaction.DrawEvent#drawend + * @api stable + */ + DRAWEND: 'drawend' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.interaction.Draw} instances are instances of + * this type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.DrawEvent} + * @param {ol.interaction.DrawEventType} type Type. + * @param {ol.Feature} feature The feature drawn. + */ +ol.interaction.DrawEvent = function(type, feature) { + + ol.events.Event.call(this, type); + + /** + * The feature being drawn. + * @type {ol.Feature} + * @api stable + */ + this.feature = feature; + +}; +ol.inherits(ol.interaction.DrawEvent, ol.events.Event); + + +/** + * @classdesc + * Interaction for drawing feature geometries. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @fires ol.interaction.DrawEvent + * @param {olx.interaction.DrawOptions} options Options. + * @api stable + */ +ol.interaction.Draw = function(options) { + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.Draw.handleDownEvent_, + handleEvent: ol.interaction.Draw.handleEvent, + handleUpEvent: ol.interaction.Draw.handleUpEvent_ + }); + + /** + * @type {ol.Pixel} + * @private + */ + this.downPx_ = null; + + /** + * @type {boolean} + * @private + */ + this.freehand_ = false; + + /** + * Target source for drawn features. + * @type {ol.source.Vector} + * @private + */ + this.source_ = options.source ? options.source : null; + + /** + * Target collection for drawn features. + * @type {ol.Collection.<ol.Feature>} + * @private + */ + this.features_ = options.features ? options.features : null; + + /** + * Pixel distance for snapping. + * @type {number} + * @private + */ + this.snapTolerance_ = options.snapTolerance ? options.snapTolerance : 12; + + /** + * Geometry type. + * @type {ol.geom.GeometryType} + * @private + */ + this.type_ = options.type; + + /** + * Drawing mode (derived from geometry type. + * @type {ol.interaction.DrawMode} + * @private + */ + this.mode_ = ol.interaction.Draw.getMode_(this.type_); + + /** + * The number of points that must be drawn before a polygon ring or line + * string can be finished. The default is 3 for polygon rings and 2 for + * line strings. + * @type {number} + * @private + */ + this.minPoints_ = options.minPoints ? + options.minPoints : + (this.mode_ === ol.interaction.DrawMode.POLYGON ? 3 : 2); + + /** + * The number of points that can be drawn before a polygon ring or line string + * is finished. The default is no restriction. + * @type {number} + * @private + */ + this.maxPoints_ = options.maxPoints ? options.maxPoints : Infinity; + + /** + * A function to decide if a potential finish coordinate is permissable + * @private + * @type {ol.EventsConditionType} + */ + this.finishCondition_ = options.finishCondition ? options.finishCondition : ol.functions.TRUE; + + var geometryFunction = options.geometryFunction; + if (!geometryFunction) { + if (this.type_ === ol.geom.GeometryType.CIRCLE) { + /** + * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates + * The coordinates. + * @param {ol.geom.SimpleGeometry=} opt_geometry Optional geometry. + * @return {ol.geom.SimpleGeometry} A geometry. + */ + geometryFunction = function(coordinates, opt_geometry) { + var circle = opt_geometry ? opt_geometry : + new ol.geom.Circle([NaN, NaN]); + goog.asserts.assertInstanceof(circle, ol.geom.Circle, + 'geometry must be an ol.geom.Circle'); + var squaredLength = ol.coordinate.squaredDistance( + coordinates[0], coordinates[1]); + circle.setCenterAndRadius(coordinates[0], Math.sqrt(squaredLength)); + return circle; + }; + } else { + var Constructor; + var mode = this.mode_; + if (mode === ol.interaction.DrawMode.POINT) { + Constructor = ol.geom.Point; + } else if (mode === ol.interaction.DrawMode.LINE_STRING) { + Constructor = ol.geom.LineString; + } else if (mode === ol.interaction.DrawMode.POLYGON) { + Constructor = ol.geom.Polygon; + } + /** + * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates + * The coordinates. + * @param {ol.geom.SimpleGeometry=} opt_geometry Optional geometry. + * @return {ol.geom.SimpleGeometry} A geometry. + */ + geometryFunction = function(coordinates, opt_geometry) { + var geometry = opt_geometry; + if (geometry) { + geometry.setCoordinates(coordinates); + } else { + geometry = new Constructor(coordinates); + } + return geometry; + }; + } + } + + /** + * @type {ol.DrawGeometryFunctionType} + * @private + */ + this.geometryFunction_ = geometryFunction; + + /** + * Finish coordinate for the feature (first point for polygons, last point for + * linestrings). + * @type {ol.Coordinate} + * @private + */ + this.finishCoordinate_ = null; + + /** + * Sketch feature. + * @type {ol.Feature} + * @private + */ + this.sketchFeature_ = null; + + /** + * Sketch point. + * @type {ol.Feature} + * @private + */ + this.sketchPoint_ = null; + + /** + * Sketch coordinates. Used when drawing a line or polygon. + * @type {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} + * @private + */ + this.sketchCoords_ = null; + + /** + * Sketch line. Used when drawing polygon. + * @type {ol.Feature} + * @private + */ + this.sketchLine_ = null; + + /** + * Sketch line coordinates. Used when drawing a polygon or circle. + * @type {Array.<ol.Coordinate>} + * @private + */ + this.sketchLineCoords_ = null; + + /** + * Squared tolerance for handling up events. If the squared distance + * between a down and up event is greater than this tolerance, up events + * will not be handled. + * @type {number} + * @private + */ + this.squaredClickTolerance_ = options.clickTolerance ? + options.clickTolerance * options.clickTolerance : 36; + + /** + * Draw overlay where our sketch features are drawn. + * @type {ol.layer.Vector} + * @private + */ + this.overlay_ = new ol.layer.Vector({ + source: new ol.source.Vector({ + useSpatialIndex: false, + wrapX: options.wrapX ? options.wrapX : false + }), + style: options.style ? options.style : + ol.interaction.Draw.getDefaultStyleFunction() + }); + + /** + * Name of the geometry attribute for newly created features. + * @type {string|undefined} + * @private + */ + this.geometryName_ = options.geometryName; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? + options.condition : ol.events.condition.noModifierKeys; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.freehandCondition_ = options.freehandCondition ? + options.freehandCondition : ol.events.condition.shiftKeyOnly; + + ol.events.listen(this, + ol.Object.getChangeEventType(ol.interaction.InteractionProperty.ACTIVE), + this.updateState_, this); + +}; +ol.inherits(ol.interaction.Draw, ol.interaction.Pointer); + + +/** + * @return {ol.StyleFunction} Styles. + */ +ol.interaction.Draw.getDefaultStyleFunction = function() { + var styles = ol.style.createDefaultEditingStyles(); + return function(feature, resolution) { + return styles[feature.getGeometry().getType()]; + }; +}; + + +/** + * @inheritDoc + */ +ol.interaction.Draw.prototype.setMap = function(map) { + ol.interaction.Pointer.prototype.setMap.call(this, map); + this.updateState_(); +}; + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} and may actually + * draw or finish the drawing. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.Draw} + * @api + */ +ol.interaction.Draw.handleEvent = function(mapBrowserEvent) { + if ((this.mode_ === ol.interaction.DrawMode.LINE_STRING || + this.mode_ === ol.interaction.DrawMode.POLYGON) && + this.freehandCondition_(mapBrowserEvent)) { + this.freehand_ = true; + } + var pass = !this.freehand_; + if (this.freehand_ && + mapBrowserEvent.type === ol.MapBrowserEvent.EventType.POINTERDRAG) { + this.addToDrawing_(mapBrowserEvent); + pass = false; + } else if (mapBrowserEvent.type === + ol.MapBrowserEvent.EventType.POINTERMOVE) { + pass = this.handlePointerMove_(mapBrowserEvent); + } else if (mapBrowserEvent.type === ol.MapBrowserEvent.EventType.DBLCLICK) { + pass = false; + } + return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && pass; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} event Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.Draw} + * @private + */ +ol.interaction.Draw.handleDownEvent_ = function(event) { + if (this.condition_(event)) { + this.downPx_ = event.pixel; + return true; + } else if (this.freehand_) { + this.downPx_ = event.pixel; + if (!this.finishCoordinate_) { + this.startDrawing_(event); + } + return true; + } else { + return false; + } +}; + + +/** + * @param {ol.MapBrowserPointerEvent} event Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.Draw} + * @private + */ +ol.interaction.Draw.handleUpEvent_ = function(event) { + this.freehand_ = false; + var downPx = this.downPx_; + var clickPx = event.pixel; + var dx = downPx[0] - clickPx[0]; + var dy = downPx[1] - clickPx[1]; + var squaredDistance = dx * dx + dy * dy; + var pass = true; + if (squaredDistance <= this.squaredClickTolerance_) { + this.handlePointerMove_(event); + if (!this.finishCoordinate_) { + this.startDrawing_(event); + if (this.mode_ === ol.interaction.DrawMode.POINT) { + this.finishDrawing(); + } + } else if (this.mode_ === ol.interaction.DrawMode.CIRCLE) { + this.finishDrawing(); + } else if (this.atFinish_(event)) { + if (this.finishCondition_(event)) { + this.finishDrawing(); + } + } else { + this.addToDrawing_(event); + } + pass = false; + } + return pass; +}; + + +/** + * Handle move events. + * @param {ol.MapBrowserEvent} event A move event. + * @return {boolean} Pass the event to other interactions. + * @private + */ +ol.interaction.Draw.prototype.handlePointerMove_ = function(event) { + if (this.finishCoordinate_) { + this.modifyDrawing_(event); + } else { + this.createOrUpdateSketchPoint_(event); + } + return true; +}; + + +/** + * Determine if an event is within the snapping tolerance of the start coord. + * @param {ol.MapBrowserEvent} event Event. + * @return {boolean} The event is within the snapping tolerance of the start. + * @private + */ +ol.interaction.Draw.prototype.atFinish_ = function(event) { + var at = false; + if (this.sketchFeature_) { + var potentiallyDone = false; + var potentiallyFinishCoordinates = [this.finishCoordinate_]; + if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { + potentiallyDone = this.sketchCoords_.length > this.minPoints_; + } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { + potentiallyDone = this.sketchCoords_[0].length > + this.minPoints_; + potentiallyFinishCoordinates = [this.sketchCoords_[0][0], + this.sketchCoords_[0][this.sketchCoords_[0].length - 2]]; + } + if (potentiallyDone) { + var map = event.map; + for (var i = 0, ii = potentiallyFinishCoordinates.length; i < ii; i++) { + var finishCoordinate = potentiallyFinishCoordinates[i]; + var finishPixel = map.getPixelFromCoordinate(finishCoordinate); + var pixel = event.pixel; + var dx = pixel[0] - finishPixel[0]; + var dy = pixel[1] - finishPixel[1]; + var freehand = this.freehand_ && this.freehandCondition_(event); + var snapTolerance = freehand ? 1 : this.snapTolerance_; + at = Math.sqrt(dx * dx + dy * dy) <= snapTolerance; + if (at) { + this.finishCoordinate_ = finishCoordinate; + break; + } + } + } + } + return at; +}; + + +/** + * @param {ol.MapBrowserEvent} event Event. + * @private + */ +ol.interaction.Draw.prototype.createOrUpdateSketchPoint_ = function(event) { + var coordinates = event.coordinate.slice(); + if (!this.sketchPoint_) { + this.sketchPoint_ = new ol.Feature(new ol.geom.Point(coordinates)); + this.updateSketchFeatures_(); + } else { + var sketchPointGeom = this.sketchPoint_.getGeometry(); + goog.asserts.assertInstanceof(sketchPointGeom, ol.geom.Point, + 'sketchPointGeom should be an ol.geom.Point'); + sketchPointGeom.setCoordinates(coordinates); + } +}; + + +/** + * Start the drawing. + * @param {ol.MapBrowserEvent} event Event. + * @private + */ +ol.interaction.Draw.prototype.startDrawing_ = function(event) { + var start = event.coordinate; + this.finishCoordinate_ = start; + if (this.mode_ === ol.interaction.DrawMode.POINT) { + this.sketchCoords_ = start.slice(); + } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { + this.sketchCoords_ = [[start.slice(), start.slice()]]; + this.sketchLineCoords_ = this.sketchCoords_[0]; + } else { + this.sketchCoords_ = [start.slice(), start.slice()]; + if (this.mode_ === ol.interaction.DrawMode.CIRCLE) { + this.sketchLineCoords_ = this.sketchCoords_; + } + } + if (this.sketchLineCoords_) { + this.sketchLine_ = new ol.Feature( + new ol.geom.LineString(this.sketchLineCoords_)); + } + var geometry = this.geometryFunction_(this.sketchCoords_); + goog.asserts.assert(geometry !== undefined, 'geometry should be defined'); + this.sketchFeature_ = new ol.Feature(); + if (this.geometryName_) { + this.sketchFeature_.setGeometryName(this.geometryName_); + } + this.sketchFeature_.setGeometry(geometry); + this.updateSketchFeatures_(); + this.dispatchEvent(new ol.interaction.DrawEvent( + ol.interaction.DrawEventType.DRAWSTART, this.sketchFeature_)); +}; + + +/** + * Modify the drawing. + * @param {ol.MapBrowserEvent} event Event. + * @private + */ +ol.interaction.Draw.prototype.modifyDrawing_ = function(event) { + var coordinate = event.coordinate; + var geometry = this.sketchFeature_.getGeometry(); + goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry, + 'geometry should be ol.geom.SimpleGeometry or subclass'); + var coordinates, last; + if (this.mode_ === ol.interaction.DrawMode.POINT) { + last = this.sketchCoords_; + } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { + coordinates = this.sketchCoords_[0]; + last = coordinates[coordinates.length - 1]; + if (this.atFinish_(event)) { + // snap to finish + coordinate = this.finishCoordinate_.slice(); + } + } else { + coordinates = this.sketchCoords_; + last = coordinates[coordinates.length - 1]; + } + last[0] = coordinate[0]; + last[1] = coordinate[1]; + goog.asserts.assert(this.sketchCoords_, 'sketchCoords_ expected'); + this.geometryFunction_(this.sketchCoords_, geometry); + if (this.sketchPoint_) { + var sketchPointGeom = this.sketchPoint_.getGeometry(); + goog.asserts.assertInstanceof(sketchPointGeom, ol.geom.Point, + 'sketchPointGeom should be an ol.geom.Point'); + sketchPointGeom.setCoordinates(coordinate); + } + var sketchLineGeom; + if (geometry instanceof ol.geom.Polygon && + this.mode_ !== ol.interaction.DrawMode.POLYGON) { + if (!this.sketchLine_) { + this.sketchLine_ = new ol.Feature(new ol.geom.LineString(null)); + } + var ring = geometry.getLinearRing(0); + sketchLineGeom = this.sketchLine_.getGeometry(); + goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString, + 'sketchLineGeom must be an ol.geom.LineString'); + sketchLineGeom.setFlatCoordinates( + ring.getLayout(), ring.getFlatCoordinates()); + } else if (this.sketchLineCoords_) { + sketchLineGeom = this.sketchLine_.getGeometry(); + goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString, + 'sketchLineGeom must be an ol.geom.LineString'); + sketchLineGeom.setCoordinates(this.sketchLineCoords_); + } + this.updateSketchFeatures_(); +}; + + +/** + * Add a new coordinate to the drawing. + * @param {ol.MapBrowserEvent} event Event. + * @private + */ +ol.interaction.Draw.prototype.addToDrawing_ = function(event) { + var coordinate = event.coordinate; + var geometry = this.sketchFeature_.getGeometry(); + goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry, + 'geometry must be an ol.geom.SimpleGeometry'); + var done; + var coordinates; + if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { + this.finishCoordinate_ = coordinate.slice(); + coordinates = this.sketchCoords_; + coordinates.push(coordinate.slice()); + done = coordinates.length > this.maxPoints_; + this.geometryFunction_(coordinates, geometry); + } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { + coordinates = this.sketchCoords_[0]; + coordinates.push(coordinate.slice()); + done = coordinates.length > this.maxPoints_; + if (done) { + this.finishCoordinate_ = coordinates[0]; + } + this.geometryFunction_(this.sketchCoords_, geometry); + } + this.updateSketchFeatures_(); + if (done) { + this.finishDrawing(); + } +}; + + +/** + * Remove last point of the feature currently being drawn. + * @api + */ +ol.interaction.Draw.prototype.removeLastPoint = function() { + var geometry = this.sketchFeature_.getGeometry(); + goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry, + 'geometry must be an ol.geom.SimpleGeometry'); + var coordinates, sketchLineGeom; + if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { + coordinates = this.sketchCoords_; + coordinates.splice(-2, 1); + this.geometryFunction_(coordinates, geometry); + } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { + coordinates = this.sketchCoords_[0]; + coordinates.splice(-2, 1); + sketchLineGeom = this.sketchLine_.getGeometry(); + goog.asserts.assertInstanceof(sketchLineGeom, ol.geom.LineString, + 'sketchLineGeom must be an ol.geom.LineString'); + sketchLineGeom.setCoordinates(coordinates); + this.geometryFunction_(this.sketchCoords_, geometry); + } + + if (coordinates.length === 0) { + this.finishCoordinate_ = null; + } + + this.updateSketchFeatures_(); +}; + + +/** + * Stop drawing and add the sketch feature to the target layer. + * The {@link ol.interaction.DrawEventType.DRAWEND} event is dispatched before + * inserting the feature. + * @api + */ +ol.interaction.Draw.prototype.finishDrawing = function() { + var sketchFeature = this.abortDrawing_(); + goog.asserts.assert(sketchFeature, 'sketchFeature expected to be truthy'); + var coordinates = this.sketchCoords_; + var geometry = sketchFeature.getGeometry(); + goog.asserts.assertInstanceof(geometry, ol.geom.SimpleGeometry, + 'geometry must be an ol.geom.SimpleGeometry'); + if (this.mode_ === ol.interaction.DrawMode.LINE_STRING) { + // remove the redundant last point + coordinates.pop(); + this.geometryFunction_(coordinates, geometry); + } else if (this.mode_ === ol.interaction.DrawMode.POLYGON) { + // When we finish drawing a polygon on the last point, + // the last coordinate is duplicated as for LineString + // we force the replacement by the first point + coordinates[0].pop(); + coordinates[0].push(coordinates[0][0]); + this.geometryFunction_(coordinates, geometry); + } + + // cast multi-part geometries + if (this.type_ === ol.geom.GeometryType.MULTI_POINT) { + sketchFeature.setGeometry(new ol.geom.MultiPoint([coordinates])); + } else if (this.type_ === ol.geom.GeometryType.MULTI_LINE_STRING) { + sketchFeature.setGeometry(new ol.geom.MultiLineString([coordinates])); + } else if (this.type_ === ol.geom.GeometryType.MULTI_POLYGON) { + sketchFeature.setGeometry(new ol.geom.MultiPolygon([coordinates])); + } + + // First dispatch event to allow full set up of feature + this.dispatchEvent(new ol.interaction.DrawEvent( + ol.interaction.DrawEventType.DRAWEND, sketchFeature)); + + // Then insert feature + if (this.features_) { + this.features_.push(sketchFeature); + } + if (this.source_) { + this.source_.addFeature(sketchFeature); + } +}; + + +/** + * Stop drawing without adding the sketch feature to the target layer. + * @return {ol.Feature} The sketch feature (or null if none). + * @private + */ +ol.interaction.Draw.prototype.abortDrawing_ = function() { + this.finishCoordinate_ = null; + var sketchFeature = this.sketchFeature_; + if (sketchFeature) { + this.sketchFeature_ = null; + this.sketchPoint_ = null; + this.sketchLine_ = null; + this.overlay_.getSource().clear(true); + } + return sketchFeature; +}; + + +/** + * Extend an existing geometry by adding additional points. This only works + * on features with `LineString` geometries, where the interaction will + * extend lines by adding points to the end of the coordinates array. + * @param {!ol.Feature} feature Feature to be extended. + * @api + */ +ol.interaction.Draw.prototype.extend = function(feature) { + var geometry = feature.getGeometry(); + goog.asserts.assert(this.mode_ == ol.interaction.DrawMode.LINE_STRING, + 'interaction mode must be "line"'); + goog.asserts.assert(geometry, 'feature must have a geometry'); + goog.asserts.assert(geometry.getType() == ol.geom.GeometryType.LINE_STRING, + 'feature geometry must be a line string'); + var lineString = /** @type {ol.geom.LineString} */ (geometry); + this.sketchFeature_ = feature; + this.sketchCoords_ = lineString.getCoordinates(); + var last = this.sketchCoords_[this.sketchCoords_.length - 1]; + this.finishCoordinate_ = last.slice(); + this.sketchCoords_.push(last.slice()); + this.updateSketchFeatures_(); + this.dispatchEvent(new ol.interaction.DrawEvent( + ol.interaction.DrawEventType.DRAWSTART, this.sketchFeature_)); +}; + + +/** + * @inheritDoc + */ +ol.interaction.Draw.prototype.shouldStopEvent = ol.functions.FALSE; + + +/** + * Redraw the sketch features. + * @private + */ +ol.interaction.Draw.prototype.updateSketchFeatures_ = function() { + var sketchFeatures = []; + if (this.sketchFeature_) { + sketchFeatures.push(this.sketchFeature_); + } + if (this.sketchLine_) { + sketchFeatures.push(this.sketchLine_); + } + if (this.sketchPoint_) { + sketchFeatures.push(this.sketchPoint_); + } + var overlaySource = this.overlay_.getSource(); + overlaySource.clear(true); + overlaySource.addFeatures(sketchFeatures); +}; + + +/** + * @private + */ +ol.interaction.Draw.prototype.updateState_ = function() { + var map = this.getMap(); + var active = this.getActive(); + if (!map || !active) { + this.abortDrawing_(); + } + this.overlay_.setMap(active ? map : null); +}; + + +/** + * Create a `geometryFunction` for `mode: 'Circle'` that will create a regular + * polygon with a user specified number of sides and start angle instead of an + * `ol.geom.Circle` geometry. + * @param {number=} opt_sides Number of sides of the regular polygon. Default is + * 32. + * @param {number=} opt_angle Angle of the first point in radians. 0 means East. + * Default is the angle defined by the heading from the center of the + * regular polygon to the current pointer position. + * @return {ol.DrawGeometryFunctionType} Function that draws a + * polygon. + * @api + */ +ol.interaction.Draw.createRegularPolygon = function(opt_sides, opt_angle) { + return ( + /** + * @param {ol.Coordinate|Array.<ol.Coordinate>|Array.<Array.<ol.Coordinate>>} coordinates + * @param {ol.geom.SimpleGeometry=} opt_geometry + * @return {ol.geom.SimpleGeometry} + */ + function(coordinates, opt_geometry) { + var center = coordinates[0]; + var end = coordinates[1]; + var radius = Math.sqrt( + ol.coordinate.squaredDistance(center, end)); + var geometry = opt_geometry ? opt_geometry : + ol.geom.Polygon.fromCircle(new ol.geom.Circle(center), opt_sides); + goog.asserts.assertInstanceof(geometry, ol.geom.Polygon, + 'geometry must be a polygon'); + var angle = opt_angle ? opt_angle : + Math.atan((end[1] - center[1]) / (end[0] - center[0])); + ol.geom.Polygon.makeRegular(geometry, center, radius, angle); + return geometry; + } + ); +}; + + +/** + * Get the drawing mode. The mode for mult-part geometries is the same as for + * their single-part cousins. + * @param {ol.geom.GeometryType} type Geometry type. + * @return {ol.interaction.DrawMode} Drawing mode. + * @private + */ +ol.interaction.Draw.getMode_ = function(type) { + var mode; + if (type === ol.geom.GeometryType.POINT || + type === ol.geom.GeometryType.MULTI_POINT) { + mode = ol.interaction.DrawMode.POINT; + } else if (type === ol.geom.GeometryType.LINE_STRING || + type === ol.geom.GeometryType.MULTI_LINE_STRING) { + mode = ol.interaction.DrawMode.LINE_STRING; + } else if (type === ol.geom.GeometryType.POLYGON || + type === ol.geom.GeometryType.MULTI_POLYGON) { + mode = ol.interaction.DrawMode.POLYGON; + } else if (type === ol.geom.GeometryType.CIRCLE) { + mode = ol.interaction.DrawMode.CIRCLE; + } + goog.asserts.assert(mode !== undefined, 'mode should be defined'); + return mode; +}; + + +/** + * Draw mode. This collapses multi-part geometry types with their single-part + * cousins. + * @enum {string} + */ +ol.interaction.DrawMode = { + POINT: 'Point', + LINE_STRING: 'LineString', + POLYGON: 'Polygon', + CIRCLE: 'Circle' +}; + +goog.provide('ol.interaction.Modify'); +goog.provide('ol.interaction.ModifyEvent'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.EventType'); +goog.require('ol'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Feature'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.MapBrowserPointerEvent'); +goog.require('ol.ViewHint'); +goog.require('ol.array'); +goog.require('ol.coordinate'); +goog.require('ol.events.condition'); +goog.require('ol.extent'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.interaction.Pointer'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.structs.RBush'); + + +/** + * @enum {string} + */ +ol.ModifyEventType = { + /** + * Triggered upon feature modification start + * @event ol.interaction.ModifyEvent#modifystart + * @api + */ + MODIFYSTART: 'modifystart', + /** + * Triggered upon feature modification end + * @event ol.interaction.ModifyEvent#modifyend + * @api + */ + MODIFYEND: 'modifyend' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.interaction.Modify} instances are instances of + * this type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.ModifyEvent} + * @param {ol.ModifyEventType} type Type. + * @param {ol.Collection.<ol.Feature>} features The features modified. + * @param {ol.MapBrowserPointerEvent} mapBrowserPointerEvent Associated + * {@link ol.MapBrowserPointerEvent}. + */ +ol.interaction.ModifyEvent = function(type, features, mapBrowserPointerEvent) { + + ol.events.Event.call(this, type); + + /** + * The features being modified. + * @type {ol.Collection.<ol.Feature>} + * @api + */ + this.features = features; + + /** + * Associated {@link ol.MapBrowserEvent}. + * @type {ol.MapBrowserEvent} + * @api + */ + this.mapBrowserEvent = mapBrowserPointerEvent; +}; +ol.inherits(ol.interaction.ModifyEvent, ol.events.Event); + + +/** + * @classdesc + * Interaction for modifying feature geometries. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.ModifyOptions} options Options. + * @fires ol.interaction.ModifyEvent + * @api + */ +ol.interaction.Modify = function(options) { + + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.Modify.handleDownEvent_, + handleDragEvent: ol.interaction.Modify.handleDragEvent_, + handleEvent: ol.interaction.Modify.handleEvent, + handleUpEvent: ol.interaction.Modify.handleUpEvent_ + }); + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? + options.condition : ol.events.condition.primaryAction; + + + /** + * @private + * @param {ol.MapBrowserEvent} mapBrowserEvent Browser event. + * @return {boolean} Combined condition result. + */ + this.defaultDeleteCondition_ = function(mapBrowserEvent) { + return ol.events.condition.noModifierKeys(mapBrowserEvent) && + ol.events.condition.singleClick(mapBrowserEvent); + }; + + /** + * @type {ol.EventsConditionType} + * @private + */ + this.deleteCondition_ = options.deleteCondition ? + options.deleteCondition : this.defaultDeleteCondition_; + + /** + * Editing vertex. + * @type {ol.Feature} + * @private + */ + this.vertexFeature_ = null; + + /** + * Segments intersecting {@link this.vertexFeature_} by segment uid. + * @type {Object.<string, boolean>} + * @private + */ + this.vertexSegments_ = null; + + /** + * @type {ol.Pixel} + * @private + */ + this.lastPixel_ = [0, 0]; + + /** + * Tracks if the next `singleclick` event should be ignored to prevent + * accidental deletion right after vertex creation. + * @type {boolean} + * @private + */ + this.ignoreNextSingleClick_ = false; + + /** + * @type {boolean} + * @private + */ + this.modified_ = false; + + /** + * Segment RTree for each layer + * @type {ol.structs.RBush.<ol.ModifySegmentDataType>} + * @private + */ + this.rBush_ = new ol.structs.RBush(); + + /** + * @type {number} + * @private + */ + this.pixelTolerance_ = options.pixelTolerance !== undefined ? + options.pixelTolerance : 10; + + /** + * @type {boolean} + * @private + */ + this.snappedToVertex_ = false; + + /** + * Indicate whether the interaction is currently changing a feature's + * coordinates. + * @type {boolean} + * @private + */ + this.changingFeature_ = false; + + /** + * @type {Array} + * @private + */ + this.dragSegments_ = []; + + /** + * Draw overlay where sketch features are drawn. + * @type {ol.layer.Vector} + * @private + */ + this.overlay_ = new ol.layer.Vector({ + source: new ol.source.Vector({ + useSpatialIndex: false, + wrapX: !!options.wrapX + }), + style: options.style ? options.style : + ol.interaction.Modify.getDefaultStyleFunction(), + updateWhileAnimating: true, + updateWhileInteracting: true + }); + + /** + * @const + * @private + * @type {Object.<string, function(ol.Feature, ol.geom.Geometry)>} + */ + this.SEGMENT_WRITERS_ = { + 'Point': this.writePointGeometry_, + 'LineString': this.writeLineStringGeometry_, + 'LinearRing': this.writeLineStringGeometry_, + 'Polygon': this.writePolygonGeometry_, + 'MultiPoint': this.writeMultiPointGeometry_, + 'MultiLineString': this.writeMultiLineStringGeometry_, + 'MultiPolygon': this.writeMultiPolygonGeometry_, + 'GeometryCollection': this.writeGeometryCollectionGeometry_ + }; + + /** + * @type {ol.Collection.<ol.Feature>} + * @private + */ + this.features_ = options.features; + + this.features_.forEach(this.addFeature_, this); + ol.events.listen(this.features_, ol.CollectionEventType.ADD, + this.handleFeatureAdd_, this); + ol.events.listen(this.features_, ol.CollectionEventType.REMOVE, + this.handleFeatureRemove_, this); + + /** + * @type {ol.MapBrowserPointerEvent} + * @private + */ + this.lastPointerEvent_ = null; + +}; +ol.inherits(ol.interaction.Modify, ol.interaction.Pointer); + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Modify.prototype.addFeature_ = function(feature) { + var geometry = feature.getGeometry(); + if (geometry.getType() in this.SEGMENT_WRITERS_) { + this.SEGMENT_WRITERS_[geometry.getType()].call(this, feature, geometry); + } + var map = this.getMap(); + if (map) { + this.handlePointerAtPixel_(this.lastPixel_, map); + } + ol.events.listen(feature, ol.events.EventType.CHANGE, + this.handleFeatureChange_, this); +}; + + +/** + * @param {ol.MapBrowserPointerEvent} evt Map browser event + * @private + */ +ol.interaction.Modify.prototype.willModifyFeatures_ = function(evt) { + if (!this.modified_) { + this.modified_ = true; + this.dispatchEvent(new ol.interaction.ModifyEvent( + ol.ModifyEventType.MODIFYSTART, this.features_, evt)); + } +}; + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Modify.prototype.removeFeature_ = function(feature) { + this.removeFeatureSegmentData_(feature); + // Remove the vertex feature if the collection of canditate features + // is empty. + if (this.vertexFeature_ && this.features_.getLength() === 0) { + this.overlay_.getSource().removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } + ol.events.unlisten(feature, ol.events.EventType.CHANGE, + this.handleFeatureChange_, this); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Modify.prototype.removeFeatureSegmentData_ = function(feature) { + var rBush = this.rBush_; + var /** @type {Array.<ol.ModifySegmentDataType>} */ nodesToRemove = []; + rBush.forEach( + /** + * @param {ol.ModifySegmentDataType} node RTree node. + */ + function(node) { + if (feature === node.feature) { + nodesToRemove.push(node); + } + }); + for (var i = nodesToRemove.length - 1; i >= 0; --i) { + rBush.remove(nodesToRemove[i]); + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.Modify.prototype.setMap = function(map) { + this.overlay_.setMap(map); + ol.interaction.Pointer.prototype.setMap.call(this, map); +}; + + +/** + * @param {ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handleFeatureAdd_ = function(evt) { + var feature = evt.element; + goog.asserts.assertInstanceof(feature, ol.Feature, + 'feature should be an ol.Feature'); + this.addFeature_(feature); +}; + + +/** + * @param {ol.events.Event} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handleFeatureChange_ = function(evt) { + if (!this.changingFeature_) { + var feature = /** @type {ol.Feature} */ (evt.target); + this.removeFeature_(feature); + this.addFeature_(feature); + } +}; + + +/** + * @param {ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handleFeatureRemove_ = function(evt) { + var feature = /** @type {ol.Feature} */ (evt.element); + this.removeFeature_(feature); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Point} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writePointGeometry_ = function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var segmentData = /** @type {ol.ModifySegmentDataType} */ ({ + feature: feature, + geometry: geometry, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPoint} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writeMultiPointGeometry_ = function(feature, geometry) { + var points = geometry.getCoordinates(); + var coordinates, i, ii, segmentData; + for (i = 0, ii = points.length; i < ii; ++i) { + coordinates = points[i]; + segmentData = /** @type {ol.ModifySegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [i], + index: i, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.LineString} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writeLineStringGeometry_ = function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var i, ii, segment, segmentData; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.ModifySegmentDataType} */ ({ + feature: feature, + geometry: geometry, + index: i, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiLineString} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) { + var lines = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = lines.length; j < jj; ++j) { + coordinates = lines[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.ModifySegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [j], + index: i, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Polygon} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writePolygonGeometry_ = function(feature, geometry) { + var rings = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.ModifySegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [j], + index: i, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPolygon} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) { + var polygons = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData; + for (k = 0, kk = polygons.length; k < kk; ++k) { + rings = polygons[k]; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.ModifySegmentDataType} */ ({ + feature: feature, + geometry: geometry, + depth: [j, k], + index: i, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.GeometryCollection} geometry Geometry. + * @private + */ +ol.interaction.Modify.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) { + var i, geometries = geometry.getGeometriesArray(); + for (i = 0; i < geometries.length; ++i) { + this.SEGMENT_WRITERS_[geometries[i].getType()].call( + this, feature, geometries[i]); + } +}; + + +/** + * @param {ol.Coordinate} coordinates Coordinates. + * @return {ol.Feature} Vertex feature. + * @private + */ +ol.interaction.Modify.prototype.createOrUpdateVertexFeature_ = function(coordinates) { + var vertexFeature = this.vertexFeature_; + if (!vertexFeature) { + vertexFeature = new ol.Feature(new ol.geom.Point(coordinates)); + this.vertexFeature_ = vertexFeature; + this.overlay_.getSource().addFeature(vertexFeature); + } else { + var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry()); + geometry.setCoordinates(coordinates); + } + return vertexFeature; +}; + + +/** + * @param {ol.ModifySegmentDataType} a The first segment data. + * @param {ol.ModifySegmentDataType} b The second segment data. + * @return {number} The difference in indexes. + * @private + */ +ol.interaction.Modify.compareIndexes_ = function(a, b) { + return a.index - b.index; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} evt Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.Modify} + * @private + */ +ol.interaction.Modify.handleDownEvent_ = function(evt) { + if (!this.condition_(evt)) { + return false; + } + this.handlePointerAtPixel_(evt.pixel, evt.map); + this.dragSegments_.length = 0; + this.modified_ = false; + var vertexFeature = this.vertexFeature_; + if (vertexFeature) { + var insertVertices = []; + var geometry = /** @type {ol.geom.Point} */ (vertexFeature.getGeometry()); + var vertex = geometry.getCoordinates(); + var vertexExtent = ol.extent.boundingExtent([vertex]); + var segmentDataMatches = this.rBush_.getInExtent(vertexExtent); + var componentSegments = {}; + segmentDataMatches.sort(ol.interaction.Modify.compareIndexes_); + for (var i = 0, ii = segmentDataMatches.length; i < ii; ++i) { + var segmentDataMatch = segmentDataMatches[i]; + var segment = segmentDataMatch.segment; + var uid = goog.getUid(segmentDataMatch.feature); + var depth = segmentDataMatch.depth; + if (depth) { + uid += '-' + depth.join('-'); // separate feature components + } + if (!componentSegments[uid]) { + componentSegments[uid] = new Array(2); + } + if (ol.coordinate.equals(segment[0], vertex) && + !componentSegments[uid][0]) { + this.dragSegments_.push([segmentDataMatch, 0]); + componentSegments[uid][0] = segmentDataMatch; + } else if (ol.coordinate.equals(segment[1], vertex) && + !componentSegments[uid][1]) { + + // prevent dragging closed linestrings by the connecting node + if ((segmentDataMatch.geometry.getType() === + ol.geom.GeometryType.LINE_STRING || + segmentDataMatch.geometry.getType() === + ol.geom.GeometryType.MULTI_LINE_STRING) && + componentSegments[uid][0] && + componentSegments[uid][0].index === 0) { + continue; + } + + this.dragSegments_.push([segmentDataMatch, 1]); + componentSegments[uid][1] = segmentDataMatch; + } else if (goog.getUid(segment) in this.vertexSegments_ && + (!componentSegments[uid][0] && !componentSegments[uid][1])) { + insertVertices.push([segmentDataMatch, vertex]); + } + } + if (insertVertices.length) { + this.willModifyFeatures_(evt); + } + for (var j = insertVertices.length - 1; j >= 0; --j) { + this.insertVertex_.apply(this, insertVertices[j]); + } + } + return !!this.vertexFeature_; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} evt Event. + * @this {ol.interaction.Modify} + * @private + */ +ol.interaction.Modify.handleDragEvent_ = function(evt) { + this.ignoreNextSingleClick_ = false; + this.willModifyFeatures_(evt); + + var vertex = evt.coordinate; + for (var i = 0, ii = this.dragSegments_.length; i < ii; ++i) { + var dragSegment = this.dragSegments_[i]; + var segmentData = dragSegment[0]; + var depth = segmentData.depth; + var geometry = segmentData.geometry; + var coordinates = geometry.getCoordinates(); + var segment = segmentData.segment; + var index = dragSegment[1]; + + while (vertex.length < geometry.getStride()) { + vertex.push(0); + } + + switch (geometry.getType()) { + case ol.geom.GeometryType.POINT: + coordinates = vertex; + segment[0] = segment[1] = vertex; + break; + case ol.geom.GeometryType.MULTI_POINT: + coordinates[segmentData.index] = vertex; + segment[0] = segment[1] = vertex; + break; + case ol.geom.GeometryType.LINE_STRING: + coordinates[segmentData.index + index] = vertex; + segment[index] = vertex; + break; + case ol.geom.GeometryType.MULTI_LINE_STRING: + coordinates[depth[0]][segmentData.index + index] = vertex; + segment[index] = vertex; + break; + case ol.geom.GeometryType.POLYGON: + coordinates[depth[0]][segmentData.index + index] = vertex; + segment[index] = vertex; + break; + case ol.geom.GeometryType.MULTI_POLYGON: + coordinates[depth[1]][depth[0]][segmentData.index + index] = vertex; + segment[index] = vertex; + break; + default: + // pass + } + + this.setGeometryCoordinates_(geometry, coordinates); + } + this.createOrUpdateVertexFeature_(vertex); +}; + + +/** + * @param {ol.MapBrowserPointerEvent} evt Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.Modify} + * @private + */ +ol.interaction.Modify.handleUpEvent_ = function(evt) { + var segmentData; + for (var i = this.dragSegments_.length - 1; i >= 0; --i) { + segmentData = this.dragSegments_[i][0]; + this.rBush_.update(ol.extent.boundingExtent(segmentData.segment), + segmentData); + } + if (this.modified_) { + this.dispatchEvent(new ol.interaction.ModifyEvent( + ol.ModifyEventType.MODIFYEND, this.features_, evt)); + this.modified_ = false; + } + return false; +}; + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} and may modify the + * geometry. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.Modify} + * @api + */ +ol.interaction.Modify.handleEvent = function(mapBrowserEvent) { + if (!(mapBrowserEvent instanceof ol.MapBrowserPointerEvent)) { + return true; + } + this.lastPointerEvent_ = mapBrowserEvent; + + var handled; + if (!mapBrowserEvent.map.getView().getHints()[ol.ViewHint.INTERACTING] && + mapBrowserEvent.type == ol.MapBrowserEvent.EventType.POINTERMOVE && + !this.handlingDownUpSequence) { + this.handlePointerMove_(mapBrowserEvent); + } + if (this.vertexFeature_ && this.deleteCondition_(mapBrowserEvent)) { + if (mapBrowserEvent.type != ol.MapBrowserEvent.EventType.SINGLECLICK || + !this.ignoreNextSingleClick_) { + var geometry = this.vertexFeature_.getGeometry(); + goog.asserts.assertInstanceof(geometry, ol.geom.Point, + 'geometry should be an ol.geom.Point'); + handled = this.removePoint(); + } else { + handled = true; + } + } + + if (mapBrowserEvent.type == ol.MapBrowserEvent.EventType.SINGLECLICK) { + this.ignoreNextSingleClick_ = false; + } + + return ol.interaction.Pointer.handleEvent.call(this, mapBrowserEvent) && + !handled; +}; + + +/** + * @param {ol.MapBrowserEvent} evt Event. + * @private + */ +ol.interaction.Modify.prototype.handlePointerMove_ = function(evt) { + this.lastPixel_ = evt.pixel; + this.handlePointerAtPixel_(evt.pixel, evt.map); +}; + + +/** + * @param {ol.Pixel} pixel Pixel + * @param {ol.Map} map Map. + * @private + */ +ol.interaction.Modify.prototype.handlePointerAtPixel_ = function(pixel, map) { + var pixelCoordinate = map.getCoordinateFromPixel(pixel); + var sortByDistance = function(a, b) { + return ol.coordinate.squaredDistanceToSegment(pixelCoordinate, a.segment) - + ol.coordinate.squaredDistanceToSegment(pixelCoordinate, b.segment); + }; + + var lowerLeft = map.getCoordinateFromPixel( + [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]); + var upperRight = map.getCoordinateFromPixel( + [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]); + var box = ol.extent.boundingExtent([lowerLeft, upperRight]); + + var rBush = this.rBush_; + var nodes = rBush.getInExtent(box); + if (nodes.length > 0) { + nodes.sort(sortByDistance); + var node = nodes[0]; + var closestSegment = node.segment; + var vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, + closestSegment)); + var vertexPixel = map.getPixelFromCoordinate(vertex); + if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <= + this.pixelTolerance_) { + var pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + var pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + var squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1); + var squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2); + var dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + this.snappedToVertex_ = dist <= this.pixelTolerance_; + if (this.snappedToVertex_) { + vertex = squaredDist1 > squaredDist2 ? + closestSegment[1] : closestSegment[0]; + } + this.createOrUpdateVertexFeature_(vertex); + var vertexSegments = {}; + vertexSegments[goog.getUid(closestSegment)] = true; + var segment; + for (var i = 1, ii = nodes.length; i < ii; ++i) { + segment = nodes[i].segment; + if ((ol.coordinate.equals(closestSegment[0], segment[0]) && + ol.coordinate.equals(closestSegment[1], segment[1]) || + (ol.coordinate.equals(closestSegment[0], segment[1]) && + ol.coordinate.equals(closestSegment[1], segment[0])))) { + vertexSegments[goog.getUid(segment)] = true; + } else { + break; + } + } + this.vertexSegments_ = vertexSegments; + return; + } + } + if (this.vertexFeature_) { + this.overlay_.getSource().removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } +}; + + +/** + * @param {ol.ModifySegmentDataType} segmentData Segment data. + * @param {ol.Coordinate} vertex Vertex. + * @private + */ +ol.interaction.Modify.prototype.insertVertex_ = function(segmentData, vertex) { + var segment = segmentData.segment; + var feature = segmentData.feature; + var geometry = segmentData.geometry; + var depth = segmentData.depth; + var index = segmentData.index; + var coordinates; + + while (vertex.length < geometry.getStride()) { + vertex.push(0); + } + + switch (geometry.getType()) { + case ol.geom.GeometryType.MULTI_LINE_STRING: + goog.asserts.assertInstanceof(geometry, ol.geom.MultiLineString, + 'geometry should be an ol.geom.MultiLineString'); + coordinates = geometry.getCoordinates(); + coordinates[depth[0]].splice(index + 1, 0, vertex); + break; + case ol.geom.GeometryType.POLYGON: + goog.asserts.assertInstanceof(geometry, ol.geom.Polygon, + 'geometry should be an ol.geom.Polygon'); + coordinates = geometry.getCoordinates(); + coordinates[depth[0]].splice(index + 1, 0, vertex); + break; + case ol.geom.GeometryType.MULTI_POLYGON: + goog.asserts.assertInstanceof(geometry, ol.geom.MultiPolygon, + 'geometry should be an ol.geom.MultiPolygon'); + coordinates = geometry.getCoordinates(); + coordinates[depth[1]][depth[0]].splice(index + 1, 0, vertex); + break; + case ol.geom.GeometryType.LINE_STRING: + goog.asserts.assertInstanceof(geometry, ol.geom.LineString, + 'geometry should be an ol.geom.LineString'); + coordinates = geometry.getCoordinates(); + coordinates.splice(index + 1, 0, vertex); + break; + default: + return; + } + + this.setGeometryCoordinates_(geometry, coordinates); + var rTree = this.rBush_; + goog.asserts.assert(segment !== undefined, 'segment should be defined'); + rTree.remove(segmentData); + goog.asserts.assert(index !== undefined, 'index should be defined'); + this.updateSegmentIndices_(geometry, index, depth, 1); + var newSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ + segment: [segment[0], vertex], + feature: feature, + geometry: geometry, + depth: depth, + index: index + }); + rTree.insert(ol.extent.boundingExtent(newSegmentData.segment), + newSegmentData); + this.dragSegments_.push([newSegmentData, 1]); + + var newSegmentData2 = /** @type {ol.ModifySegmentDataType} */ ({ + segment: [vertex, segment[1]], + feature: feature, + geometry: geometry, + depth: depth, + index: index + 1 + }); + rTree.insert(ol.extent.boundingExtent(newSegmentData2.segment), + newSegmentData2); + this.dragSegments_.push([newSegmentData2, 0]); + this.ignoreNextSingleClick_ = true; +}; + +/** + * Removes the vertex currently being pointed. + * @return {boolean} True when a vertex was removed. + * @api + */ +ol.interaction.Modify.prototype.removePoint = function() { + var handled = false; + if (this.lastPointerEvent_ && this.lastPointerEvent_.type != ol.MapBrowserEvent.EventType.POINTERDRAG) { + var evt = this.lastPointerEvent_; + this.willModifyFeatures_(evt); + handled = this.removeVertex_(); + this.dispatchEvent(new ol.interaction.ModifyEvent( + ol.ModifyEventType.MODIFYEND, this.features_, evt)); + this.modified_ = false; + } + return handled; +}; + +/** + * Removes a vertex from all matching features. + * @return {boolean} True when a vertex was removed. + * @private + */ +ol.interaction.Modify.prototype.removeVertex_ = function() { + var dragSegments = this.dragSegments_; + var segmentsByFeature = {}; + var component, coordinates, dragSegment, geometry, i, index, left; + var newIndex, right, segmentData, uid, deleted; + for (i = dragSegments.length - 1; i >= 0; --i) { + dragSegment = dragSegments[i]; + segmentData = dragSegment[0]; + uid = goog.getUid(segmentData.feature); + if (segmentData.depth) { + // separate feature components + uid += '-' + segmentData.depth.join('-'); + } + if (!(uid in segmentsByFeature)) { + segmentsByFeature[uid] = {}; + } + if (dragSegment[1] === 0) { + segmentsByFeature[uid].right = segmentData; + segmentsByFeature[uid].index = segmentData.index; + } else if (dragSegment[1] == 1) { + segmentsByFeature[uid].left = segmentData; + segmentsByFeature[uid].index = segmentData.index + 1; + } + + } + for (uid in segmentsByFeature) { + right = segmentsByFeature[uid].right; + left = segmentsByFeature[uid].left; + index = segmentsByFeature[uid].index; + newIndex = index - 1; + if (left !== undefined) { + segmentData = left; + } else { + segmentData = right; + } + if (newIndex < 0) { + newIndex = 0; + } + geometry = segmentData.geometry; + coordinates = geometry.getCoordinates(); + component = coordinates; + deleted = false; + switch (geometry.getType()) { + case ol.geom.GeometryType.MULTI_LINE_STRING: + if (coordinates[segmentData.depth[0]].length > 2) { + coordinates[segmentData.depth[0]].splice(index, 1); + deleted = true; + } + break; + case ol.geom.GeometryType.LINE_STRING: + if (coordinates.length > 2) { + coordinates.splice(index, 1); + deleted = true; + } + break; + case ol.geom.GeometryType.MULTI_POLYGON: + component = component[segmentData.depth[1]]; + /* falls through */ + case ol.geom.GeometryType.POLYGON: + component = component[segmentData.depth[0]]; + if (component.length > 4) { + if (index == component.length - 1) { + index = 0; + } + component.splice(index, 1); + deleted = true; + if (index === 0) { + // close the ring again + component.pop(); + component.push(component[0]); + newIndex = component.length - 1; + } + } + break; + default: + // pass + } + + if (deleted) { + this.setGeometryCoordinates_(geometry, coordinates); + var segments = []; + if (left !== undefined) { + this.rBush_.remove(left); + segments.push(left.segment[0]); + } + if (right !== undefined) { + this.rBush_.remove(right); + segments.push(right.segment[1]); + } + if (left !== undefined && right !== undefined) { + goog.asserts.assert(newIndex >= 0, 'newIndex should be larger than 0'); + + var newSegmentData = /** @type {ol.ModifySegmentDataType} */ ({ + depth: segmentData.depth, + feature: segmentData.feature, + geometry: segmentData.geometry, + index: newIndex, + segment: segments + }); + this.rBush_.insert(ol.extent.boundingExtent(newSegmentData.segment), + newSegmentData); + } + this.updateSegmentIndices_(geometry, index, segmentData.depth, -1); + if (this.vertexFeature_) { + this.overlay_.getSource().removeFeature(this.vertexFeature_); + this.vertexFeature_ = null; + } + } + + } + return true; +}; + + +/** + * @param {ol.geom.SimpleGeometry} geometry Geometry. + * @param {Array} coordinates Coordinates. + * @private + */ +ol.interaction.Modify.prototype.setGeometryCoordinates_ = function(geometry, coordinates) { + this.changingFeature_ = true; + geometry.setCoordinates(coordinates); + this.changingFeature_ = false; +}; + + +/** + * @param {ol.geom.SimpleGeometry} geometry Geometry. + * @param {number} index Index. + * @param {Array.<number>|undefined} depth Depth. + * @param {number} delta Delta (1 or -1). + * @private + */ +ol.interaction.Modify.prototype.updateSegmentIndices_ = function( + geometry, index, depth, delta) { + this.rBush_.forEachInExtent(geometry.getExtent(), function(segmentDataMatch) { + if (segmentDataMatch.geometry === geometry && + (depth === undefined || segmentDataMatch.depth === undefined || + ol.array.equals(segmentDataMatch.depth, depth)) && + segmentDataMatch.index > index) { + segmentDataMatch.index += delta; + } + }); +}; + + +/** + * @return {ol.StyleFunction} Styles. + */ +ol.interaction.Modify.getDefaultStyleFunction = function() { + var style = ol.style.createDefaultEditingStyles(); + return function(feature, resolution) { + return style[ol.geom.GeometryType.POINT]; + }; +}; + +goog.provide('ol.interaction.Select'); +goog.provide('ol.interaction.SelectEvent'); +goog.provide('ol.interaction.SelectEventType'); + +goog.require('goog.asserts'); +goog.require('ol.functions'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Feature'); +goog.require('ol.array'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.condition'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.layer.Vector'); +goog.require('ol.object'); +goog.require('ol.source.Vector'); + + +/** + * @enum {string} + */ +ol.interaction.SelectEventType = { + /** + * Triggered when feature(s) has been (de)selected. + * @event ol.interaction.SelectEvent#select + * @api + */ + SELECT: 'select' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.interaction.Select} instances are instances of + * this type. + * + * @param {string} type The event type. + * @param {Array.<ol.Feature>} selected Selected features. + * @param {Array.<ol.Feature>} deselected Deselected features. + * @param {ol.MapBrowserEvent} mapBrowserEvent Associated + * {@link ol.MapBrowserEvent}. + * @implements {oli.SelectEvent} + * @extends {ol.events.Event} + * @constructor + */ +ol.interaction.SelectEvent = function(type, selected, deselected, mapBrowserEvent) { + ol.events.Event.call(this, type); + + /** + * Selected features array. + * @type {Array.<ol.Feature>} + * @api + */ + this.selected = selected; + + /** + * Deselected features array. + * @type {Array.<ol.Feature>} + * @api + */ + this.deselected = deselected; + + /** + * Associated {@link ol.MapBrowserEvent}. + * @type {ol.MapBrowserEvent} + * @api + */ + this.mapBrowserEvent = mapBrowserEvent; +}; +ol.inherits(ol.interaction.SelectEvent, ol.events.Event); + + +/** + * @classdesc + * Interaction for selecting vector features. By default, selected features are + * styled differently, so this interaction can be used for visual highlighting, + * as well as selecting features for other actions, such as modification or + * output. There are three ways of controlling which features are selected: + * using the browser event as defined by the `condition` and optionally the + * `toggle`, `add`/`remove`, and `multi` options; a `layers` filter; and a + * further feature filter using the `filter` option. + * + * Selected features are added to an internal unmanaged layer. + * + * @constructor + * @extends {ol.interaction.Interaction} + * @param {olx.interaction.SelectOptions=} opt_options Options. + * @fires ol.interaction.SelectEvent + * @api stable + */ +ol.interaction.Select = function(opt_options) { + + ol.interaction.Interaction.call(this, { + handleEvent: ol.interaction.Select.handleEvent + }); + + var options = opt_options ? opt_options : {}; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.condition_ = options.condition ? + options.condition : ol.events.condition.singleClick; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.addCondition_ = options.addCondition ? + options.addCondition : ol.events.condition.never; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.removeCondition_ = options.removeCondition ? + options.removeCondition : ol.events.condition.never; + + /** + * @private + * @type {ol.EventsConditionType} + */ + this.toggleCondition_ = options.toggleCondition ? + options.toggleCondition : ol.events.condition.shiftKeyOnly; + + /** + * @private + * @type {boolean} + */ + this.multi_ = options.multi ? options.multi : false; + + /** + * @private + * @type {ol.SelectFilterFunction} + */ + this.filter_ = options.filter ? options.filter : + ol.functions.TRUE; + + var featureOverlay = new ol.layer.Vector({ + source: new ol.source.Vector({ + useSpatialIndex: false, + features: options.features, + wrapX: options.wrapX + }), + style: options.style ? options.style : + ol.interaction.Select.getDefaultStyleFunction(), + updateWhileAnimating: true, + updateWhileInteracting: true + }); + + /** + * @private + * @type {ol.layer.Vector} + */ + this.featureOverlay_ = featureOverlay; + + var layerFilter; + if (options.layers) { + if (typeof options.layers === 'function') { + /** + * @param {ol.layer.Layer} layer Layer. + * @return {boolean} Include. + */ + layerFilter = function(layer) { + goog.asserts.assertFunction(options.layers); + return options.layers(layer); + }; + } else { + var layers = options.layers; + /** + * @param {ol.layer.Layer} layer Layer. + * @return {boolean} Include. + */ + layerFilter = function(layer) { + return ol.array.includes(layers, layer); + }; + } + } else { + layerFilter = ol.functions.TRUE; + } + + /** + * @private + * @type {function(ol.layer.Layer): boolean} + */ + this.layerFilter_ = layerFilter; + + /** + * An association between selected feature (key) + * and layer (value) + * @private + * @type {Object.<number, ol.layer.Layer>} + */ + this.featureLayerAssociation_ = {}; + + var features = this.featureOverlay_.getSource().getFeaturesCollection(); + ol.events.listen(features, ol.CollectionEventType.ADD, + this.addFeature_, this); + ol.events.listen(features, ol.CollectionEventType.REMOVE, + this.removeFeature_, this); + +}; +ol.inherits(ol.interaction.Select, ol.interaction.Interaction); + + +/** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {ol.layer.Layer} layer Layer. + * @private + */ +ol.interaction.Select.prototype.addFeatureLayerAssociation_ = function(feature, layer) { + var key = goog.getUid(feature); + this.featureLayerAssociation_[key] = layer; +}; + + +/** + * Get the selected features. + * @return {ol.Collection.<ol.Feature>} Features collection. + * @api stable + */ +ol.interaction.Select.prototype.getFeatures = function() { + return this.featureOverlay_.getSource().getFeaturesCollection(); +}; + + +/** + * Returns the associated {@link ol.layer.Vector vectorlayer} of + * the (last) selected feature. Note that this will not work with any + * programmatic method like pushing features to + * {@link ol.interaction.Select#getFeatures collection}. + * @param {ol.Feature|ol.render.Feature} feature Feature + * @return {ol.layer.Vector} Layer. + * @api + */ +ol.interaction.Select.prototype.getLayer = function(feature) { + goog.asserts.assertInstanceof(feature, ol.Feature, + 'feature should be an ol.Feature'); + var key = goog.getUid(feature); + return /** @type {ol.layer.Vector} */ (this.featureLayerAssociation_[key]); +}; + + +/** + * Handles the {@link ol.MapBrowserEvent map browser event} and may change the + * selected state of features. + * @param {ol.MapBrowserEvent} mapBrowserEvent Map browser event. + * @return {boolean} `false` to stop event propagation. + * @this {ol.interaction.Select} + * @api + */ +ol.interaction.Select.handleEvent = function(mapBrowserEvent) { + if (!this.condition_(mapBrowserEvent)) { + return true; + } + var add = this.addCondition_(mapBrowserEvent); + var remove = this.removeCondition_(mapBrowserEvent); + var toggle = this.toggleCondition_(mapBrowserEvent); + var set = !add && !remove && !toggle; + var map = mapBrowserEvent.map; + var features = this.featureOverlay_.getSource().getFeaturesCollection(); + var deselected = []; + var selected = []; + if (set) { + // Replace the currently selected feature(s) with the feature(s) at the + // pixel, or clear the selected feature(s) if there is no feature at + // the pixel. + ol.object.clear(this.featureLayerAssociation_); + map.forEachFeatureAtPixel(mapBrowserEvent.pixel, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {ol.layer.Layer} layer Layer. + * @return {boolean|undefined} Continue to iterate over the features. + */ + function(feature, layer) { + if (this.filter_(feature, layer)) { + selected.push(feature); + this.addFeatureLayerAssociation_(feature, layer); + return !this.multi_; + } + }, this, this.layerFilter_); + if (selected.length > 0 && features.getLength() == 1 && features.item(0) == selected[0]) { + // No change; an already selected feature is selected again + selected.length = 0; + } else { + if (features.getLength() !== 0) { + deselected = Array.prototype.concat(features.getArray()); + features.clear(); + } + features.extend(selected); + } + } else { + // Modify the currently selected feature(s). + map.forEachFeatureAtPixel(mapBrowserEvent.pixel, + /** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @param {ol.layer.Layer} layer Layer. + * @return {boolean|undefined} Continue to iterate over the features. + */ + function(feature, layer) { + if (this.filter_(feature, layer)) { + if ((add || toggle) && + !ol.array.includes(features.getArray(), feature)) { + selected.push(feature); + this.addFeatureLayerAssociation_(feature, layer); + } else if ((remove || toggle) && + ol.array.includes(features.getArray(), feature)) { + deselected.push(feature); + this.removeFeatureLayerAssociation_(feature); + } + return !this.multi_; + } + }, this, this.layerFilter_); + var i; + for (i = deselected.length - 1; i >= 0; --i) { + features.remove(deselected[i]); + } + features.extend(selected); + } + if (selected.length > 0 || deselected.length > 0) { + this.dispatchEvent( + new ol.interaction.SelectEvent(ol.interaction.SelectEventType.SELECT, + selected, deselected, mapBrowserEvent)); + } + return ol.events.condition.pointerMove(mapBrowserEvent); +}; + + +/** + * Remove the interaction from its current map, if any, and attach it to a new + * map, if any. Pass `null` to just remove the interaction from the current map. + * @param {ol.Map} map Map. + * @api stable + */ +ol.interaction.Select.prototype.setMap = function(map) { + var currentMap = this.getMap(); + var selectedFeatures = + this.featureOverlay_.getSource().getFeaturesCollection(); + if (currentMap) { + selectedFeatures.forEach(currentMap.unskipFeature, currentMap); + } + ol.interaction.Interaction.prototype.setMap.call(this, map); + this.featureOverlay_.setMap(map); + if (map) { + selectedFeatures.forEach(map.skipFeature, map); + } +}; + + +/** + * @return {ol.StyleFunction} Styles. + */ +ol.interaction.Select.getDefaultStyleFunction = function() { + var styles = ol.style.createDefaultEditingStyles(); + ol.array.extend(styles[ol.geom.GeometryType.POLYGON], + styles[ol.geom.GeometryType.LINE_STRING]); + ol.array.extend(styles[ol.geom.GeometryType.GEOMETRY_COLLECTION], + styles[ol.geom.GeometryType.LINE_STRING]); + + return function(feature, resolution) { + return styles[feature.getGeometry().getType()]; + }; +}; + + +/** + * @param {ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Select.prototype.addFeature_ = function(evt) { + var feature = evt.element; + var map = this.getMap(); + goog.asserts.assertInstanceof(feature, ol.Feature, + 'feature should be an ol.Feature'); + if (map) { + map.skipFeature(feature); + } +}; + + +/** + * @param {ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Select.prototype.removeFeature_ = function(evt) { + var feature = evt.element; + var map = this.getMap(); + goog.asserts.assertInstanceof(feature, ol.Feature, + 'feature should be an ol.Feature'); + if (map) { + map.unskipFeature(feature); + } +}; + + +/** + * @param {ol.Feature|ol.render.Feature} feature Feature. + * @private + */ +ol.interaction.Select.prototype.removeFeatureLayerAssociation_ = function(feature) { + var key = goog.getUid(feature); + delete this.featureLayerAssociation_[key]; +}; + +goog.provide('ol.interaction.Snap'); +goog.provide('ol.interaction.SnapProperty'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEvent'); +goog.require('ol.CollectionEventType'); +goog.require('ol.Feature'); +goog.require('ol.Object'); +goog.require('ol.Observable'); +goog.require('ol.coordinate'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.geom.Geometry'); +goog.require('ol.interaction.Pointer'); +goog.require('ol.functions'); +goog.require('ol.object'); +goog.require('ol.source.Vector'); +goog.require('ol.source.VectorEvent'); +goog.require('ol.source.VectorEventType'); +goog.require('ol.structs.RBush'); + + +/** + * @classdesc + * Handles snapping of vector features while modifying or drawing them. The + * features can come from a {@link ol.source.Vector} or {@link ol.Collection} + * Any interaction object that allows the user to interact + * with the features using the mouse can benefit from the snapping, as long + * as it is added before. + * + * The snap interaction modifies map browser event `coordinate` and `pixel` + * properties to force the snap to occur to any interaction that them. + * + * Example: + * + * var snap = new ol.interaction.Snap({ + * source: source + * }); + * + * @constructor + * @extends {ol.interaction.Pointer} + * @param {olx.interaction.SnapOptions=} opt_options Options. + * @api + */ +ol.interaction.Snap = function(opt_options) { + + ol.interaction.Pointer.call(this, { + handleEvent: ol.interaction.Snap.handleEvent_, + handleDownEvent: ol.functions.TRUE, + handleUpEvent: ol.interaction.Snap.handleUpEvent_ + }); + + var options = opt_options ? opt_options : {}; + + /** + * @type {ol.source.Vector} + * @private + */ + this.source_ = options.source ? options.source : null; + + /** + * @private + * @type {boolean} + */ + this.vertex_ = options.vertex !== undefined ? options.vertex : true; + + /** + * @private + * @type {boolean} + */ + this.edge_ = options.edge !== undefined ? options.edge : true; + + /** + * @type {ol.Collection.<ol.Feature>} + * @private + */ + this.features_ = options.features ? options.features : null; + + /** + * @type {Array.<ol.EventsKey>} + * @private + */ + this.featuresListenerKeys_ = []; + + /** + * @type {Object.<number, ol.EventsKey>} + * @private + */ + this.geometryChangeListenerKeys_ = {}; + + /** + * @type {Object.<number, ol.EventsKey>} + * @private + */ + this.geometryModifyListenerKeys_ = {}; + + /** + * Extents are preserved so indexed segment can be quickly removed + * when its feature geometry changes + * @type {Object.<number, ol.Extent>} + * @private + */ + this.indexedFeaturesExtents_ = {}; + + /** + * If a feature geometry changes while a pointer drag|move event occurs, the + * feature doesn't get updated right away. It will be at the next 'pointerup' + * event fired. + * @type {Object.<number, ol.Feature>} + * @private + */ + this.pendingFeatures_ = {}; + + /** + * Used for distance sorting in sortByDistance_ + * @type {ol.Coordinate} + * @private + */ + this.pixelCoordinate_ = null; + + /** + * @type {number} + * @private + */ + this.pixelTolerance_ = options.pixelTolerance !== undefined ? + options.pixelTolerance : 10; + + /** + * @type {function(ol.SnapSegmentDataType, ol.SnapSegmentDataType): number} + * @private + */ + this.sortByDistance_ = ol.interaction.Snap.sortByDistance.bind(this); + + + /** + * Segment RTree for each layer + * @type {ol.structs.RBush.<ol.SnapSegmentDataType>} + * @private + */ + this.rBush_ = new ol.structs.RBush(); + + + /** + * @const + * @private + * @type {Object.<string, function(ol.Feature, ol.geom.Geometry)>} + */ + this.SEGMENT_WRITERS_ = { + 'Point': this.writePointGeometry_, + 'LineString': this.writeLineStringGeometry_, + 'LinearRing': this.writeLineStringGeometry_, + 'Polygon': this.writePolygonGeometry_, + 'MultiPoint': this.writeMultiPointGeometry_, + 'MultiLineString': this.writeMultiLineStringGeometry_, + 'MultiPolygon': this.writeMultiPolygonGeometry_, + 'GeometryCollection': this.writeGeometryCollectionGeometry_ + }; +}; +ol.inherits(ol.interaction.Snap, ol.interaction.Pointer); + + +/** + * Add a feature to the collection of features that we may snap to. + * @param {ol.Feature} feature Feature. + * @param {boolean=} opt_listen Whether to listen to the geometry change or not + * Defaults to `true`. + * @api + */ +ol.interaction.Snap.prototype.addFeature = function(feature, opt_listen) { + var listen = opt_listen !== undefined ? opt_listen : true; + var feature_uid = goog.getUid(feature); + var geometry = feature.getGeometry(); + if (geometry) { + var segmentWriter = this.SEGMENT_WRITERS_[geometry.getType()]; + if (segmentWriter) { + this.indexedFeaturesExtents_[feature_uid] = geometry.getExtent( + ol.extent.createEmpty()); + segmentWriter.call(this, feature, geometry); + + if (listen) { + this.geometryModifyListenerKeys_[feature_uid] = ol.events.listen( + geometry, + ol.events.EventType.CHANGE, + this.handleGeometryModify_.bind(this, feature), + this); + } + } + } + + if (listen) { + this.geometryChangeListenerKeys_[feature_uid] = ol.events.listen( + feature, + ol.Object.getChangeEventType(feature.getGeometryName()), + this.handleGeometryChange_, this); + } +}; + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Snap.prototype.forEachFeatureAdd_ = function(feature) { + this.addFeature(feature); +}; + + +/** + * @param {ol.Feature} feature Feature. + * @private + */ +ol.interaction.Snap.prototype.forEachFeatureRemove_ = function(feature) { + this.removeFeature(feature); +}; + + +/** + * @return {ol.Collection.<ol.Feature>|Array.<ol.Feature>} Features. + * @private + */ +ol.interaction.Snap.prototype.getFeatures_ = function() { + var features; + if (this.features_) { + features = this.features_; + } else if (this.source_) { + features = this.source_.getFeatures(); + } + goog.asserts.assert(features !== undefined, 'features should be defined'); + return features; +}; + + +/** + * @param {ol.source.VectorEvent|ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Snap.prototype.handleFeatureAdd_ = function(evt) { + var feature; + if (evt instanceof ol.source.VectorEvent) { + feature = evt.feature; + } else if (evt instanceof ol.CollectionEvent) { + feature = evt.element; + } + goog.asserts.assertInstanceof(feature, ol.Feature, + 'feature should be an ol.Feature'); + this.addFeature(feature); +}; + + +/** + * @param {ol.source.VectorEvent|ol.CollectionEvent} evt Event. + * @private + */ +ol.interaction.Snap.prototype.handleFeatureRemove_ = function(evt) { + var feature; + if (evt instanceof ol.source.VectorEvent) { + feature = evt.feature; + } else if (evt instanceof ol.CollectionEvent) { + feature = evt.element; + } + goog.asserts.assertInstanceof(feature, ol.Feature, + 'feature should be an ol.Feature'); + this.removeFeature(feature); +}; + + +/** + * @param {ol.events.Event} evt Event. + * @private + */ +ol.interaction.Snap.prototype.handleGeometryChange_ = function(evt) { + var feature = evt.target; + goog.asserts.assertInstanceof(feature, ol.Feature); + this.removeFeature(feature, true); + this.addFeature(feature, true); +}; + + +/** + * @param {ol.Feature} feature Feature which geometry was modified. + * @param {ol.events.Event} evt Event. + * @private + */ +ol.interaction.Snap.prototype.handleGeometryModify_ = function(feature, evt) { + if (this.handlingDownUpSequence) { + var uid = goog.getUid(feature); + if (!(uid in this.pendingFeatures_)) { + this.pendingFeatures_[uid] = feature; + } + } else { + this.updateFeature_(feature); + } +}; + + +/** + * Remove a feature from the collection of features that we may snap to. + * @param {ol.Feature} feature Feature + * @param {boolean=} opt_unlisten Whether to unlisten to the geometry change + * or not. Defaults to `true`. + * @api + */ +ol.interaction.Snap.prototype.removeFeature = function(feature, opt_unlisten) { + var unlisten = opt_unlisten !== undefined ? opt_unlisten : true; + var feature_uid = goog.getUid(feature); + var extent = this.indexedFeaturesExtents_[feature_uid]; + if (extent) { + var rBush = this.rBush_; + var i, nodesToRemove = []; + rBush.forEachInExtent(extent, function(node) { + if (feature === node.feature) { + nodesToRemove.push(node); + } + }); + for (i = nodesToRemove.length - 1; i >= 0; --i) { + rBush.remove(nodesToRemove[i]); + } + if (unlisten) { + ol.Observable.unByKey(this.geometryModifyListenerKeys_[feature_uid]); + delete this.geometryModifyListenerKeys_[feature_uid]; + } + } + + if (unlisten) { + ol.Observable.unByKey(this.geometryChangeListenerKeys_[feature_uid]); + delete this.geometryChangeListenerKeys_[feature_uid]; + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.Snap.prototype.setMap = function(map) { + var currentMap = this.getMap(); + var keys = this.featuresListenerKeys_; + var features = this.getFeatures_(); + + if (currentMap) { + keys.forEach(ol.Observable.unByKey); + keys.length = 0; + features.forEach(this.forEachFeatureRemove_, this); + } + ol.interaction.Pointer.prototype.setMap.call(this, map); + + if (map) { + if (this.features_) { + keys.push( + ol.events.listen(this.features_, ol.CollectionEventType.ADD, + this.handleFeatureAdd_, this), + ol.events.listen(this.features_, ol.CollectionEventType.REMOVE, + this.handleFeatureRemove_, this) + ); + } else if (this.source_) { + keys.push( + ol.events.listen(this.source_, ol.source.VectorEventType.ADDFEATURE, + this.handleFeatureAdd_, this), + ol.events.listen(this.source_, ol.source.VectorEventType.REMOVEFEATURE, + this.handleFeatureRemove_, this) + ); + } + features.forEach(this.forEachFeatureAdd_, this); + } +}; + + +/** + * @inheritDoc + */ +ol.interaction.Snap.prototype.shouldStopEvent = ol.functions.FALSE; + + +/** + * @param {ol.Pixel} pixel Pixel + * @param {ol.Coordinate} pixelCoordinate Coordinate + * @param {ol.Map} map Map. + * @return {ol.SnapResultType} Snap result + */ +ol.interaction.Snap.prototype.snapTo = function(pixel, pixelCoordinate, map) { + + var lowerLeft = map.getCoordinateFromPixel( + [pixel[0] - this.pixelTolerance_, pixel[1] + this.pixelTolerance_]); + var upperRight = map.getCoordinateFromPixel( + [pixel[0] + this.pixelTolerance_, pixel[1] - this.pixelTolerance_]); + var box = ol.extent.boundingExtent([lowerLeft, upperRight]); + + var segments = this.rBush_.getInExtent(box); + var snappedToVertex = false; + var snapped = false; + var vertex = null; + var vertexPixel = null; + var dist, pixel1, pixel2, squaredDist1, squaredDist2; + if (segments.length > 0) { + this.pixelCoordinate_ = pixelCoordinate; + segments.sort(this.sortByDistance_); + var closestSegment = segments[0].segment; + if (this.vertex_ && !this.edge_) { + pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + squaredDist1 = ol.coordinate.squaredDistance(pixel, pixel1); + squaredDist2 = ol.coordinate.squaredDistance(pixel, pixel2); + dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + snappedToVertex = dist <= this.pixelTolerance_; + if (snappedToVertex) { + snapped = true; + vertex = squaredDist1 > squaredDist2 ? + closestSegment[1] : closestSegment[0]; + vertexPixel = map.getPixelFromCoordinate(vertex); + } + } else if (this.edge_) { + vertex = (ol.coordinate.closestOnSegment(pixelCoordinate, + closestSegment)); + vertexPixel = map.getPixelFromCoordinate(vertex); + if (Math.sqrt(ol.coordinate.squaredDistance(pixel, vertexPixel)) <= + this.pixelTolerance_) { + snapped = true; + if (this.vertex_) { + pixel1 = map.getPixelFromCoordinate(closestSegment[0]); + pixel2 = map.getPixelFromCoordinate(closestSegment[1]); + squaredDist1 = ol.coordinate.squaredDistance(vertexPixel, pixel1); + squaredDist2 = ol.coordinate.squaredDistance(vertexPixel, pixel2); + dist = Math.sqrt(Math.min(squaredDist1, squaredDist2)); + snappedToVertex = dist <= this.pixelTolerance_; + if (snappedToVertex) { + vertex = squaredDist1 > squaredDist2 ? + closestSegment[1] : closestSegment[0]; + vertexPixel = map.getPixelFromCoordinate(vertex); + } + } + } + } + if (snapped) { + vertexPixel = [Math.round(vertexPixel[0]), Math.round(vertexPixel[1])]; + } + } + return /** @type {ol.SnapResultType} */ ({ + snapped: snapped, + vertex: vertex, + vertexPixel: vertexPixel + }); +}; + + +/** + * @param {ol.Feature} feature Feature + * @private + */ +ol.interaction.Snap.prototype.updateFeature_ = function(feature) { + this.removeFeature(feature, false); + this.addFeature(feature, false); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.GeometryCollection} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeGeometryCollectionGeometry_ = function(feature, geometry) { + var i, geometries = geometry.getGeometriesArray(); + for (i = 0; i < geometries.length; ++i) { + this.SEGMENT_WRITERS_[geometries[i].getType()].call( + this, feature, geometries[i]); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.LineString} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeLineStringGeometry_ = function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var i, ii, segment, segmentData; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.SnapSegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiLineString} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiLineStringGeometry_ = function(feature, geometry) { + var lines = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = lines.length; j < jj; ++j) { + coordinates = lines[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.SnapSegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPoint} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiPointGeometry_ = function(feature, geometry) { + var points = geometry.getCoordinates(); + var coordinates, i, ii, segmentData; + for (i = 0, ii = points.length; i < ii; ++i) { + coordinates = points[i]; + segmentData = /** @type {ol.SnapSegmentDataType} */ ({ + feature: feature, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.MultiPolygon} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writeMultiPolygonGeometry_ = function(feature, geometry) { + var polygons = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, k, kk, rings, segment, segmentData; + for (k = 0, kk = polygons.length; k < kk; ++k) { + rings = polygons[k]; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.SnapSegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } + } +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Point} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writePointGeometry_ = function(feature, geometry) { + var coordinates = geometry.getCoordinates(); + var segmentData = /** @type {ol.SnapSegmentDataType} */ ({ + feature: feature, + segment: [coordinates, coordinates] + }); + this.rBush_.insert(geometry.getExtent(), segmentData); +}; + + +/** + * @param {ol.Feature} feature Feature + * @param {ol.geom.Polygon} geometry Geometry. + * @private + */ +ol.interaction.Snap.prototype.writePolygonGeometry_ = function(feature, geometry) { + var rings = geometry.getCoordinates(); + var coordinates, i, ii, j, jj, segment, segmentData; + for (j = 0, jj = rings.length; j < jj; ++j) { + coordinates = rings[j]; + for (i = 0, ii = coordinates.length - 1; i < ii; ++i) { + segment = coordinates.slice(i, i + 2); + segmentData = /** @type {ol.SnapSegmentDataType} */ ({ + feature: feature, + segment: segment + }); + this.rBush_.insert(ol.extent.boundingExtent(segment), segmentData); + } + } +}; + + +/** + * Handle all pointer events events. + * @param {ol.MapBrowserEvent} evt A move event. + * @return {boolean} Pass the event to other interactions. + * @this {ol.interaction.Snap} + * @private + */ +ol.interaction.Snap.handleEvent_ = function(evt) { + var result = this.snapTo(evt.pixel, evt.coordinate, evt.map); + if (result.snapped) { + evt.coordinate = result.vertex.slice(0, 2); + evt.pixel = result.vertexPixel; + } + return ol.interaction.Pointer.handleEvent.call(this, evt); +}; + + +/** + * @param {ol.MapBrowserPointerEvent} evt Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.Snap} + * @private + */ +ol.interaction.Snap.handleUpEvent_ = function(evt) { + var featuresToUpdate = ol.object.getValues(this.pendingFeatures_); + if (featuresToUpdate.length) { + featuresToUpdate.forEach(this.updateFeature_, this); + this.pendingFeatures_ = {}; + } + return false; +}; + + +/** + * Sort segments by distance, helper function + * @param {ol.SnapSegmentDataType} a The first segment data. + * @param {ol.SnapSegmentDataType} b The second segment data. + * @return {number} The difference in distance. + * @this {ol.interaction.Snap} + */ +ol.interaction.Snap.sortByDistance = function(a, b) { + return ol.coordinate.squaredDistanceToSegment( + this.pixelCoordinate_, a.segment) - + ol.coordinate.squaredDistanceToSegment( + this.pixelCoordinate_, b.segment); +}; + +goog.provide('ol.interaction.Translate'); +goog.provide('ol.interaction.TranslateEvent'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.array'); +goog.require('ol.interaction.Pointer'); + + +/** + * @enum {string} + */ +ol.interaction.TranslateEventType = { + /** + * Triggered upon feature translation start. + * @event ol.interaction.TranslateEvent#translatestart + * @api + */ + TRANSLATESTART: 'translatestart', + /** + * Triggered upon feature translation. + * @event ol.interaction.TranslateEvent#translating + * @api + */ + TRANSLATING: 'translating', + /** + * Triggered upon feature translation end. + * @event ol.interaction.TranslateEvent#translateend + * @api + */ + TRANSLATEEND: 'translateend' +}; + + +/** + * @classdesc + * Events emitted by {@link ol.interaction.Translate} instances are instances of + * this type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.interaction.TranslateEvent} + * @param {ol.interaction.TranslateEventType} type Type. + * @param {ol.Collection.<ol.Feature>} features The features translated. + * @param {ol.Coordinate} coordinate The event coordinate. + */ +ol.interaction.TranslateEvent = function(type, features, coordinate) { + + ol.events.Event.call(this, type); + + /** + * The features being translated. + * @type {ol.Collection.<ol.Feature>} + * @api + */ + this.features = features; + + /** + * The coordinate of the drag event. + * @const + * @type {ol.Coordinate} + * @api + */ + this.coordinate = coordinate; +}; +ol.inherits(ol.interaction.TranslateEvent, ol.events.Event); + + +/** + * @classdesc + * Interaction for translating (moving) features. + * + * @constructor + * @extends {ol.interaction.Pointer} + * @fires ol.interaction.TranslateEvent + * @param {olx.interaction.TranslateOptions} options Options. + * @api + */ +ol.interaction.Translate = function(options) { + ol.interaction.Pointer.call(this, { + handleDownEvent: ol.interaction.Translate.handleDownEvent_, + handleDragEvent: ol.interaction.Translate.handleDragEvent_, + handleMoveEvent: ol.interaction.Translate.handleMoveEvent_, + handleUpEvent: ol.interaction.Translate.handleUpEvent_ + }); + + + /** + * @type {string|undefined} + * @private + */ + this.previousCursor_ = undefined; + + + /** + * The last position we translated to. + * @type {ol.Coordinate} + * @private + */ + this.lastCoordinate_ = null; + + + /** + * @type {ol.Collection.<ol.Feature>} + * @private + */ + this.features_ = options.features !== undefined ? options.features : null; + + var layerFilter; + if (options.layers) { + if (typeof options.layers === 'function') { + /** + * @param {ol.layer.Layer} layer Layer. + * @return {boolean} Include. + */ + layerFilter = function(layer) { + goog.asserts.assertFunction(options.layers); + return options.layers(layer); + }; + } else { + var layers = options.layers; + /** + * @param {ol.layer.Layer} layer Layer. + * @return {boolean} Include. + */ + layerFilter = function(layer) { + return ol.array.includes(layers, layer); + }; + } + } else { + layerFilter = ol.functions.TRUE; + } + + /** + * @private + * @type {function(ol.layer.Layer): boolean} + */ + this.layerFilter_ = layerFilter; + + /** + * @type {ol.Feature} + * @private + */ + this.lastFeature_ = null; +}; +ol.inherits(ol.interaction.Translate, ol.interaction.Pointer); + + +/** + * @param {ol.MapBrowserPointerEvent} event Event. + * @return {boolean} Start drag sequence? + * @this {ol.interaction.Translate} + * @private + */ +ol.interaction.Translate.handleDownEvent_ = function(event) { + this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map); + if (!this.lastCoordinate_ && this.lastFeature_) { + this.lastCoordinate_ = event.coordinate; + ol.interaction.Translate.handleMoveEvent_.call(this, event); + this.dispatchEvent( + new ol.interaction.TranslateEvent( + ol.interaction.TranslateEventType.TRANSLATESTART, this.features_, + event.coordinate)); + return true; + } + return false; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} event Event. + * @return {boolean} Stop drag sequence? + * @this {ol.interaction.Translate} + * @private + */ +ol.interaction.Translate.handleUpEvent_ = function(event) { + if (this.lastCoordinate_) { + this.lastCoordinate_ = null; + ol.interaction.Translate.handleMoveEvent_.call(this, event); + this.dispatchEvent( + new ol.interaction.TranslateEvent( + ol.interaction.TranslateEventType.TRANSLATEEND, this.features_, + event.coordinate)); + return true; + } + return false; +}; + + +/** + * @param {ol.MapBrowserPointerEvent} event Event. + * @this {ol.interaction.Translate} + * @private + */ +ol.interaction.Translate.handleDragEvent_ = function(event) { + if (this.lastCoordinate_) { + var newCoordinate = event.coordinate; + var deltaX = newCoordinate[0] - this.lastCoordinate_[0]; + var deltaY = newCoordinate[1] - this.lastCoordinate_[1]; + + if (this.features_) { + this.features_.forEach(function(feature) { + var geom = feature.getGeometry(); + geom.translate(deltaX, deltaY); + feature.setGeometry(geom); + }); + } else if (this.lastFeature_) { + var geom = this.lastFeature_.getGeometry(); + geom.translate(deltaX, deltaY); + this.lastFeature_.setGeometry(geom); + } + + this.lastCoordinate_ = newCoordinate; + this.dispatchEvent( + new ol.interaction.TranslateEvent( + ol.interaction.TranslateEventType.TRANSLATING, this.features_, + newCoordinate)); + } +}; + + +/** + * @param {ol.MapBrowserEvent} event Event. + * @this {ol.interaction.Translate} + * @private + */ +ol.interaction.Translate.handleMoveEvent_ = function(event) { + var elem = event.map.getTargetElement(); + var intersectingFeature = event.map.forEachFeatureAtPixel(event.pixel, + function(feature) { + return feature; + }); + + if (intersectingFeature) { + var isSelected = false; + + if (this.features_ && + ol.array.includes(this.features_.getArray(), intersectingFeature)) { + isSelected = true; + } + + this.previousCursor_ = elem.style.cursor; + + // WebKit browsers don't support the grab icons without a prefix + elem.style.cursor = this.lastCoordinate_ ? + '-webkit-grabbing' : (isSelected ? '-webkit-grab' : 'pointer'); + + // Thankfully, attempting to set the standard ones will silently fail, + // keeping the prefixed icons + elem.style.cursor = !this.lastCoordinate_ ? + 'grabbing' : (isSelected ? 'grab' : 'pointer'); + + } else { + elem.style.cursor = this.previousCursor_ !== undefined ? + this.previousCursor_ : ''; + this.previousCursor_ = undefined; + } +}; + + +/** + * Tests to see if the given coordinates intersects any of our selected + * features. + * @param {ol.Pixel} pixel Pixel coordinate to test for intersection. + * @param {ol.Map} map Map to test the intersection on. + * @return {ol.Feature} Returns the feature found at the specified pixel + * coordinates. + * @private + */ +ol.interaction.Translate.prototype.featuresAtPixel_ = function(pixel, map) { + var found = null; + + var intersectingFeature = map.forEachFeatureAtPixel(pixel, + function(feature) { + return feature; + }, this, this.layerFilter_); + + if (this.features_ && + ol.array.includes(this.features_.getArray(), intersectingFeature)) { + found = intersectingFeature; + } + + return found; +}; + +goog.provide('ol.layer.Heatmap'); + +goog.require('goog.asserts'); +goog.require('ol.events'); +goog.require('ol'); +goog.require('ol.Object'); +goog.require('ol.dom'); +goog.require('ol.layer.Vector'); +goog.require('ol.math'); +goog.require('ol.object'); +goog.require('ol.render.EventType'); +goog.require('ol.style.Icon'); +goog.require('ol.style.Style'); + + +/** + * @enum {string} + */ +ol.layer.HeatmapLayerProperty = { + BLUR: 'blur', + GRADIENT: 'gradient', + RADIUS: 'radius' +}; + + +/** + * @classdesc + * Layer for rendering vector data as a heatmap. + * Note that any property set in the options is set as a {@link ol.Object} + * property on the layer object; for example, setting `title: 'My Title'` in the + * options means that `title` is observable, and has get/set accessors. + * + * @constructor + * @extends {ol.layer.Vector} + * @fires ol.render.Event + * @param {olx.layer.HeatmapOptions=} opt_options Options. + * @api + */ +ol.layer.Heatmap = function(opt_options) { + var options = opt_options ? opt_options : {}; + + var baseOptions = ol.object.assign({}, options); + + delete baseOptions.gradient; + delete baseOptions.radius; + delete baseOptions.blur; + delete baseOptions.shadow; + delete baseOptions.weight; + ol.layer.Vector.call(this, /** @type {olx.layer.VectorOptions} */ (baseOptions)); + + /** + * @private + * @type {Uint8ClampedArray} + */ + this.gradient_ = null; + + /** + * @private + * @type {number} + */ + this.shadow_ = options.shadow !== undefined ? options.shadow : 250; + + /** + * @private + * @type {string|undefined} + */ + this.circleImage_ = undefined; + + /** + * @private + * @type {Array.<Array.<ol.style.Style>>} + */ + this.styleCache_ = null; + + ol.events.listen(this, + ol.Object.getChangeEventType(ol.layer.HeatmapLayerProperty.GRADIENT), + this.handleGradientChanged_, this); + + this.setGradient(options.gradient ? + options.gradient : ol.layer.Heatmap.DEFAULT_GRADIENT); + + this.setBlur(options.blur !== undefined ? options.blur : 15); + + this.setRadius(options.radius !== undefined ? options.radius : 8); + + ol.events.listen(this, + ol.Object.getChangeEventType(ol.layer.HeatmapLayerProperty.BLUR), + this.handleStyleChanged_, this); + ol.events.listen(this, + ol.Object.getChangeEventType(ol.layer.HeatmapLayerProperty.RADIUS), + this.handleStyleChanged_, this); + + this.handleStyleChanged_(); + + var weight = options.weight ? options.weight : 'weight'; + var weightFunction; + if (typeof weight === 'string') { + weightFunction = function(feature) { + return feature.get(weight); + }; + } else { + weightFunction = weight; + } + goog.asserts.assert(typeof weightFunction === 'function', + 'weightFunction should be a function'); + + this.setStyle(function(feature, resolution) { + goog.asserts.assert(this.styleCache_, 'this.styleCache_ expected'); + goog.asserts.assert(this.circleImage_ !== undefined, + 'this.circleImage_ should be defined'); + var weight = weightFunction(feature); + var opacity = weight !== undefined ? ol.math.clamp(weight, 0, 1) : 1; + // cast to 8 bits + var index = (255 * opacity) | 0; + var style = this.styleCache_[index]; + if (!style) { + style = [ + new ol.style.Style({ + image: new ol.style.Icon({ + opacity: opacity, + src: this.circleImage_ + }) + }) + ]; + this.styleCache_[index] = style; + } + return style; + }.bind(this)); + + // For performance reasons, don't sort the features before rendering. + // The render order is not relevant for a heatmap representation. + this.setRenderOrder(null); + + ol.events.listen(this, ol.render.EventType.RENDER, this.handleRender_, this); + +}; +ol.inherits(ol.layer.Heatmap, ol.layer.Vector); + + +/** + * @const + * @type {Array.<string>} + */ +ol.layer.Heatmap.DEFAULT_GRADIENT = ['#00f', '#0ff', '#0f0', '#ff0', '#f00']; + + +/** + * @param {Array.<string>} colors A list of colored. + * @return {Uint8ClampedArray} An array. + * @private + */ +ol.layer.Heatmap.createGradient_ = function(colors) { + var width = 1; + var height = 256; + var context = ol.dom.createCanvasContext2D(width, height); + + var gradient = context.createLinearGradient(0, 0, width, height); + var step = 1 / (colors.length - 1); + for (var i = 0, ii = colors.length; i < ii; ++i) { + gradient.addColorStop(i * step, colors[i]); + } + + context.fillStyle = gradient; + context.fillRect(0, 0, width, height); + + return context.getImageData(0, 0, width, height).data; +}; + + +/** + * @return {string} Data URL for a circle. + * @private + */ +ol.layer.Heatmap.prototype.createCircle_ = function() { + var radius = this.getRadius(); + var blur = this.getBlur(); + goog.asserts.assert(radius !== undefined && blur !== undefined, + 'radius and blur should be defined'); + var halfSize = radius + blur + 1; + var size = 2 * halfSize; + var context = ol.dom.createCanvasContext2D(size, size); + context.shadowOffsetX = context.shadowOffsetY = this.shadow_; + context.shadowBlur = blur; + context.shadowColor = '#000'; + context.beginPath(); + var center = halfSize - this.shadow_; + context.arc(center, center, radius, 0, Math.PI * 2, true); + context.fill(); + return context.canvas.toDataURL(); +}; + + +/** + * Return the blur size in pixels. + * @return {number} Blur size in pixels. + * @api + * @observable + */ +ol.layer.Heatmap.prototype.getBlur = function() { + return /** @type {number} */ (this.get(ol.layer.HeatmapLayerProperty.BLUR)); +}; + + +/** + * Return the gradient colors as array of strings. + * @return {Array.<string>} Colors. + * @api + * @observable + */ +ol.layer.Heatmap.prototype.getGradient = function() { + return /** @type {Array.<string>} */ ( + this.get(ol.layer.HeatmapLayerProperty.GRADIENT)); +}; + + +/** + * Return the size of the radius in pixels. + * @return {number} Radius size in pixel. + * @api + * @observable + */ +ol.layer.Heatmap.prototype.getRadius = function() { + return /** @type {number} */ (this.get(ol.layer.HeatmapLayerProperty.RADIUS)); +}; + + +/** + * @private + */ +ol.layer.Heatmap.prototype.handleGradientChanged_ = function() { + this.gradient_ = ol.layer.Heatmap.createGradient_(this.getGradient()); +}; + + +/** + * @private + */ +ol.layer.Heatmap.prototype.handleStyleChanged_ = function() { + this.circleImage_ = this.createCircle_(); + this.styleCache_ = new Array(256); + this.changed(); +}; + + +/** + * @param {ol.render.Event} event Post compose event + * @private + */ +ol.layer.Heatmap.prototype.handleRender_ = function(event) { + goog.asserts.assert(event.type == ol.render.EventType.RENDER, + 'event.type should be RENDER'); + goog.asserts.assert(this.gradient_, 'this.gradient_ expected'); + var context = event.context; + var canvas = context.canvas; + var image = context.getImageData(0, 0, canvas.width, canvas.height); + var view8 = image.data; + var i, ii, alpha; + for (i = 0, ii = view8.length; i < ii; i += 4) { + alpha = view8[i + 3] * 4; + if (alpha) { + view8[i] = this.gradient_[alpha]; + view8[i + 1] = this.gradient_[alpha + 1]; + view8[i + 2] = this.gradient_[alpha + 2]; + } + } + context.putImageData(image, 0, 0); +}; + + +/** + * Set the blur size in pixels. + * @param {number} blur Blur size in pixels. + * @api + * @observable + */ +ol.layer.Heatmap.prototype.setBlur = function(blur) { + this.set(ol.layer.HeatmapLayerProperty.BLUR, blur); +}; + + +/** + * Set the gradient colors as array of strings. + * @param {Array.<string>} colors Gradient. + * @api + * @observable + */ +ol.layer.Heatmap.prototype.setGradient = function(colors) { + this.set(ol.layer.HeatmapLayerProperty.GRADIENT, colors); +}; + + +/** + * Set the size of the radius in pixels. + * @param {number} radius Radius size in pixel. + * @api + * @observable + */ +ol.layer.Heatmap.prototype.setRadius = function(radius) { + this.set(ol.layer.HeatmapLayerProperty.RADIUS, radius); +}; + +goog.provide('ol.net'); + + +/** + * Simple JSONP helper. Supports error callbacks and a custom callback param. + * The error callback will be called when no JSONP is executed after 10 seconds. + * + * @param {string} url Request url. A 'callback' query parameter will be + * appended. + * @param {Function} callback Callback on success. + * @param {function()=} opt_errback Callback on error. + * @param {string=} opt_callbackParam Custom query parameter for the JSONP + * callback. Default is 'callback'. + */ +ol.net.jsonp = function(url, callback, opt_errback, opt_callbackParam) { + var script = ol.global.document.createElement('script'); + var key = 'olc_' + goog.getUid(callback); + function cleanup() { + delete ol.global[key]; + script.parentNode.removeChild(script); + } + script.async = true; + script.src = url + (url.indexOf('?') == -1 ? '?' : '&') + + (opt_callbackParam || 'callback') + '=' + key; + var timer = ol.global.setTimeout(function() { + cleanup(); + if (opt_errback) { + opt_errback(); + } + }, 10000); + ol.global[key] = function(data) { + ol.global.clearTimeout(timer); + cleanup(); + callback(data); + }; + ol.global.document.getElementsByTagName('head')[0].appendChild(script); +}; + +goog.provide('ol.render'); + +goog.require('goog.vec.Mat4'); +goog.require('ol.render.canvas.Immediate'); +goog.require('ol.vec.Mat4'); + + +/** + * Binds a Canvas Immediate API to a canvas context, to allow drawing geometries + * to the context's canvas. + * + * The units for geometry coordinates are css pixels relative to the top left + * corner of the canvas element. + * ```js + * var canvas = document.createElement('canvas'); + * var render = ol.render.toContext(canvas.getContext('2d'), + * { size: [100, 100] }); + * render.setFillStrokeStyle(new ol.style.Fill({ color: blue })); + * render.drawPolygon( + * new ol.geom.Polygon([[[0, 0], [100, 100], [100, 0], [0, 0]]])); + * ``` + * + * @param {CanvasRenderingContext2D} context Canvas context. + * @param {olx.render.ToContextOptions=} opt_options Options. + * @return {ol.render.canvas.Immediate} Canvas Immediate. + * @api + */ +ol.render.toContext = function(context, opt_options) { + var canvas = context.canvas; + var options = opt_options ? opt_options : {}; + var pixelRatio = options.pixelRatio || ol.has.DEVICE_PIXEL_RATIO; + var size = options.size; + if (size) { + canvas.width = size[0] * pixelRatio; + canvas.height = size[1] * pixelRatio; + canvas.style.width = size[0] + 'px'; + canvas.style.height = size[1] + 'px'; + } + var extent = [0, 0, canvas.width, canvas.height]; + var transform = ol.vec.Mat4.makeTransform2D(goog.vec.Mat4.createNumber(), + 0, 0, pixelRatio, pixelRatio, 0, 0, 0); + return new ol.render.canvas.Immediate(context, pixelRatio, extent, transform, + 0); +}; + +goog.provide('ol.reproj.Tile'); + +goog.require('goog.asserts'); +goog.require('ol.Tile'); +goog.require('ol.TileState'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.math'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.reproj'); +goog.require('ol.reproj.Triangulation'); + + +/** + * @classdesc + * Class encapsulating single reprojected tile. + * See {@link ol.source.TileImage}. + * + * @constructor + * @extends {ol.Tile} + * @param {ol.proj.Projection} sourceProj Source projection. + * @param {ol.tilegrid.TileGrid} sourceTileGrid Source tile grid. + * @param {ol.proj.Projection} targetProj Target projection. + * @param {ol.tilegrid.TileGrid} targetTileGrid Target tile grid. + * @param {ol.TileCoord} tileCoord Coordinate of the tile. + * @param {ol.TileCoord} wrappedTileCoord Coordinate of the tile wrapped in X. + * @param {number} pixelRatio Pixel ratio. + * @param {number} gutter Gutter of the source tiles. + * @param {ol.ReprojTileFunctionType} getTileFunction + * Function returning source tiles (z, x, y, pixelRatio). + * @param {number=} opt_errorThreshold Acceptable reprojection error (in px). + * @param {boolean=} opt_renderEdges Render reprojection edges. + */ +ol.reproj.Tile = function(sourceProj, sourceTileGrid, + targetProj, targetTileGrid, tileCoord, wrappedTileCoord, + pixelRatio, gutter, getTileFunction, + opt_errorThreshold, + opt_renderEdges) { + ol.Tile.call(this, tileCoord, ol.TileState.IDLE); + + /** + * @private + * @type {boolean} + */ + this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false; + + /** + * @private + * @type {number} + */ + this.pixelRatio_ = pixelRatio; + + /** + * @private + * @type {number} + */ + this.gutter_ = gutter; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; + + /** + * @private + * @type {Object.<number, HTMLCanvasElement>} + */ + this.canvasByContext_ = {}; + + /** + * @private + * @type {ol.tilegrid.TileGrid} + */ + this.sourceTileGrid_ = sourceTileGrid; + + /** + * @private + * @type {ol.tilegrid.TileGrid} + */ + this.targetTileGrid_ = targetTileGrid; + + /** + * @private + * @type {ol.TileCoord} + */ + this.wrappedTileCoord_ = wrappedTileCoord ? wrappedTileCoord : tileCoord; + + /** + * @private + * @type {!Array.<ol.Tile>} + */ + this.sourceTiles_ = []; + + /** + * @private + * @type {Array.<ol.EventsKey>} + */ + this.sourcesListenerKeys_ = null; + + /** + * @private + * @type {number} + */ + this.sourceZ_ = 0; + + var targetExtent = targetTileGrid.getTileCoordExtent(this.wrappedTileCoord_); + var maxTargetExtent = this.targetTileGrid_.getExtent(); + var maxSourceExtent = this.sourceTileGrid_.getExtent(); + + var limitedTargetExtent = maxTargetExtent ? + ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent; + + if (ol.extent.getArea(limitedTargetExtent) === 0) { + // Tile is completely outside range -> EMPTY + // TODO: is it actually correct that the source even creates the tile ? + this.state = ol.TileState.EMPTY; + return; + } + + var sourceProjExtent = sourceProj.getExtent(); + if (sourceProjExtent) { + if (!maxSourceExtent) { + maxSourceExtent = sourceProjExtent; + } else { + maxSourceExtent = ol.extent.getIntersection( + maxSourceExtent, sourceProjExtent); + } + } + + var targetResolution = targetTileGrid.getResolution( + this.wrappedTileCoord_[0]); + + var targetCenter = ol.extent.getCenter(limitedTargetExtent); + var sourceResolution = ol.reproj.calculateSourceResolution( + sourceProj, targetProj, targetCenter, targetResolution); + + if (!isFinite(sourceResolution) || sourceResolution <= 0) { + // invalid sourceResolution -> EMPTY + // probably edges of the projections when no extent is defined + this.state = ol.TileState.EMPTY; + return; + } + + var errorThresholdInPixels = opt_errorThreshold !== undefined ? + opt_errorThreshold : ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD; + + /** + * @private + * @type {!ol.reproj.Triangulation} + */ + this.triangulation_ = new ol.reproj.Triangulation( + sourceProj, targetProj, limitedTargetExtent, maxSourceExtent, + sourceResolution * errorThresholdInPixels); + + if (this.triangulation_.getTriangles().length === 0) { + // no valid triangles -> EMPTY + this.state = ol.TileState.EMPTY; + return; + } + + this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution); + var sourceExtent = this.triangulation_.calculateSourceExtent(); + + if (maxSourceExtent) { + if (sourceProj.canWrapX()) { + sourceExtent[1] = ol.math.clamp( + sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]); + sourceExtent[3] = ol.math.clamp( + sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]); + } else { + sourceExtent = ol.extent.getIntersection(sourceExtent, maxSourceExtent); + } + } + + if (!ol.extent.getArea(sourceExtent)) { + this.state = ol.TileState.EMPTY; + } else { + var sourceRange = sourceTileGrid.getTileRangeForExtentAndZ( + sourceExtent, this.sourceZ_); + + var tilesRequired = sourceRange.getWidth() * sourceRange.getHeight(); + if (!goog.asserts.assert( + tilesRequired < ol.RASTER_REPROJECTION_MAX_SOURCE_TILES, + 'reasonable number of tiles is required')) { + this.state = ol.TileState.ERROR; + return; + } + for (var srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) { + for (var srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) { + var tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio); + if (tile) { + this.sourceTiles_.push(tile); + } + } + } + + if (this.sourceTiles_.length === 0) { + this.state = ol.TileState.EMPTY; + } + } +}; +ol.inherits(ol.reproj.Tile, ol.Tile); + + +/** + * @inheritDoc + */ +ol.reproj.Tile.prototype.disposeInternal = function() { + if (this.state == ol.TileState.LOADING) { + this.unlistenSources_(); + } + ol.Tile.prototype.disposeInternal.call(this); +}; + + +/** + * @inheritDoc + */ +ol.reproj.Tile.prototype.getImage = function(opt_context) { + if (opt_context !== undefined) { + var image; + var key = goog.getUid(opt_context); + if (key in this.canvasByContext_) { + return this.canvasByContext_[key]; + } else if (ol.object.isEmpty(this.canvasByContext_)) { + image = this.canvas_; + } else { + image = /** @type {HTMLCanvasElement} */ (this.canvas_.cloneNode(false)); + } + this.canvasByContext_[key] = image; + return image; + } else { + return this.canvas_; + } +}; + + +/** + * @private + */ +ol.reproj.Tile.prototype.reproject_ = function() { + var sources = []; + this.sourceTiles_.forEach(function(tile, i, arr) { + if (tile && tile.getState() == ol.TileState.LOADED) { + sources.push({ + extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord), + image: tile.getImage() + }); + } + }, this); + this.sourceTiles_.length = 0; + + if (sources.length === 0) { + this.state = ol.TileState.ERROR; + } else { + var z = this.wrappedTileCoord_[0]; + var size = this.targetTileGrid_.getTileSize(z); + var width = goog.isNumber(size) ? size : size[0]; + var height = goog.isNumber(size) ? size : size[1]; + var targetResolution = this.targetTileGrid_.getResolution(z); + var sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_); + + var targetExtent = this.targetTileGrid_.getTileCoordExtent( + this.wrappedTileCoord_); + this.canvas_ = ol.reproj.render(width, height, this.pixelRatio_, + sourceResolution, this.sourceTileGrid_.getExtent(), + targetResolution, targetExtent, this.triangulation_, sources, + this.gutter_, this.renderEdges_); + + this.state = ol.TileState.LOADED; + } + this.changed(); +}; + + +/** + * @inheritDoc + */ +ol.reproj.Tile.prototype.load = function() { + if (this.state == ol.TileState.IDLE) { + this.state = ol.TileState.LOADING; + this.changed(); + + var leftToLoad = 0; + + goog.asserts.assert(!this.sourcesListenerKeys_, + 'this.sourcesListenerKeys_ should be null'); + + this.sourcesListenerKeys_ = []; + this.sourceTiles_.forEach(function(tile, i, arr) { + var state = tile.getState(); + if (state == ol.TileState.IDLE || state == ol.TileState.LOADING) { + leftToLoad++; + + var sourceListenKey; + sourceListenKey = ol.events.listen(tile, ol.events.EventType.CHANGE, + function(e) { + var state = tile.getState(); + if (state == ol.TileState.LOADED || + state == ol.TileState.ERROR || + state == ol.TileState.EMPTY) { + ol.events.unlistenByKey(sourceListenKey); + leftToLoad--; + goog.asserts.assert(leftToLoad >= 0, + 'leftToLoad should not be negative'); + if (leftToLoad === 0) { + this.unlistenSources_(); + this.reproject_(); + } + } + }, this); + this.sourcesListenerKeys_.push(sourceListenKey); + } + }, this); + + this.sourceTiles_.forEach(function(tile, i, arr) { + var state = tile.getState(); + if (state == ol.TileState.IDLE) { + tile.load(); + } + }); + + if (leftToLoad === 0) { + ol.global.setTimeout(this.reproject_.bind(this), 0); + } + } +}; + + +/** + * @private + */ +ol.reproj.Tile.prototype.unlistenSources_ = function() { + goog.asserts.assert(this.sourcesListenerKeys_, + 'this.sourcesListenerKeys_ should not be null'); + this.sourcesListenerKeys_.forEach(ol.events.unlistenByKey); + this.sourcesListenerKeys_ = null; +}; + +goog.provide('ol.source.TileImage'); + +goog.require('goog.asserts'); +goog.require('ol.ImageTile'); +goog.require('ol.TileCache'); +goog.require('ol.TileState'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.proj'); +goog.require('ol.reproj.Tile'); +goog.require('ol.source.UrlTile'); + + +/** + * @classdesc + * Base class for sources providing images divided into a tile grid. + * + * @constructor + * @fires ol.source.TileEvent + * @extends {ol.source.UrlTile} + * @param {olx.source.TileImageOptions} options Image tile options. + * @api + */ +ol.source.TileImage = function(options) { + + ol.source.UrlTile.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + extent: options.extent, + logo: options.logo, + opaque: options.opaque, + projection: options.projection, + state: options.state, + tileGrid: options.tileGrid, + tileLoadFunction: options.tileLoadFunction ? + options.tileLoadFunction : ol.source.TileImage.defaultTileLoadFunction, + tilePixelRatio: options.tilePixelRatio, + tileUrlFunction: options.tileUrlFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX + }); + + /** + * @protected + * @type {?string} + */ + this.crossOrigin = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @protected + * @type {function(new: ol.ImageTile, ol.TileCoord, ol.TileState, string, + * ?string, ol.TileLoadFunctionType)} + */ + this.tileClass = options.tileClass !== undefined ? + options.tileClass : ol.ImageTile; + + /** + * @protected + * @type {Object.<string, ol.TileCache>} + */ + this.tileCacheForProjection = {}; + + /** + * @protected + * @type {Object.<string, ol.tilegrid.TileGrid>} + */ + this.tileGridForProjection = {}; + + /** + * @private + * @type {number|undefined} + */ + this.reprojectionErrorThreshold_ = options.reprojectionErrorThreshold; + + /** + * @private + * @type {boolean} + */ + this.renderReprojectionEdges_ = false; +}; +ol.inherits(ol.source.TileImage, ol.source.UrlTile); + + +/** + * @inheritDoc + */ +ol.source.TileImage.prototype.canExpireCache = function() { + if (!ol.ENABLE_RASTER_REPROJECTION) { + return ol.source.UrlTile.prototype.canExpireCache.call(this); + } + if (this.tileCache.canExpireCache()) { + return true; + } else { + for (var key in this.tileCacheForProjection) { + if (this.tileCacheForProjection[key].canExpireCache()) { + return true; + } + } + } + return false; +}; + + +/** + * @inheritDoc + */ +ol.source.TileImage.prototype.expireCache = function(projection, usedTiles) { + if (!ol.ENABLE_RASTER_REPROJECTION) { + ol.source.UrlTile.prototype.expireCache.call(this, projection, usedTiles); + return; + } + var usedTileCache = this.getTileCacheForProjection(projection); + + this.tileCache.expireCache(this.tileCache == usedTileCache ? usedTiles : {}); + for (var id in this.tileCacheForProjection) { + var tileCache = this.tileCacheForProjection[id]; + tileCache.expireCache(tileCache == usedTileCache ? usedTiles : {}); + } +}; + + +/** + * @inheritDoc + */ +ol.source.TileImage.prototype.getGutter = function(projection) { + if (ol.ENABLE_RASTER_REPROJECTION && + this.getProjection() && projection && + !ol.proj.equivalent(this.getProjection(), projection)) { + return 0; + } else { + return this.getGutterInternal(); + } +}; + + +/** + * @protected + * @return {number} Gutter. + */ +ol.source.TileImage.prototype.getGutterInternal = function() { + return 0; +}; + + +/** + * @inheritDoc + */ +ol.source.TileImage.prototype.getOpaque = function(projection) { + if (ol.ENABLE_RASTER_REPROJECTION && + this.getProjection() && projection && + !ol.proj.equivalent(this.getProjection(), projection)) { + return false; + } else { + return ol.source.UrlTile.prototype.getOpaque.call(this, projection); + } +}; + + +/** + * @inheritDoc + */ +ol.source.TileImage.prototype.getTileGridForProjection = function(projection) { + if (!ol.ENABLE_RASTER_REPROJECTION) { + return ol.source.UrlTile.prototype.getTileGridForProjection.call(this, projection); + } + var thisProj = this.getProjection(); + if (this.tileGrid && + (!thisProj || ol.proj.equivalent(thisProj, projection))) { + return this.tileGrid; + } else { + var projKey = goog.getUid(projection).toString(); + if (!(projKey in this.tileGridForProjection)) { + this.tileGridForProjection[projKey] = + ol.tilegrid.getForProjection(projection); + } + return this.tileGridForProjection[projKey]; + } +}; + + +/** + * @inheritDoc + */ +ol.source.TileImage.prototype.getTileCacheForProjection = function(projection) { + if (!ol.ENABLE_RASTER_REPROJECTION) { + return ol.source.UrlTile.prototype.getTileCacheForProjection.call(this, projection); + } + var thisProj = this.getProjection(); + if (!thisProj || ol.proj.equivalent(thisProj, projection)) { + return this.tileCache; + } else { + var projKey = goog.getUid(projection).toString(); + if (!(projKey in this.tileCacheForProjection)) { + this.tileCacheForProjection[projKey] = new ol.TileCache(); + } + return this.tileCacheForProjection[projKey]; + } +}; + + +/** + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @param {string} key The key set on the tile. + * @return {ol.Tile} Tile. + * @private + */ +ol.source.TileImage.prototype.createTile_ = function(z, x, y, pixelRatio, projection, key) { + var tileCoord = [z, x, y]; + var urlTileCoord = this.getTileCoordForTileUrlFunction( + tileCoord, projection); + var tileUrl = urlTileCoord ? + this.tileUrlFunction(urlTileCoord, pixelRatio, projection) : undefined; + var tile = new this.tileClass( + tileCoord, + tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, + tileUrl !== undefined ? tileUrl : '', + this.crossOrigin, + this.tileLoadFunction); + tile.key = key; + ol.events.listen(tile, ol.events.EventType.CHANGE, + this.handleTileChange, this); + return tile; +}; + + +/** + * @inheritDoc + */ +ol.source.TileImage.prototype.getTile = function(z, x, y, pixelRatio, projection) { + if (!ol.ENABLE_RASTER_REPROJECTION || + !this.getProjection() || + !projection || + ol.proj.equivalent(this.getProjection(), projection)) { + return this.getTileInternal(z, x, y, pixelRatio, projection); + } else { + var cache = this.getTileCacheForProjection(projection); + var tileCoord = [z, x, y]; + var tileCoordKey = this.getKeyZXY.apply(this, tileCoord); + if (cache.containsKey(tileCoordKey)) { + return /** @type {!ol.Tile} */ (cache.get(tileCoordKey)); + } else { + var sourceProjection = this.getProjection(); + var sourceTileGrid = this.getTileGridForProjection(sourceProjection); + var targetTileGrid = this.getTileGridForProjection(projection); + var wrappedTileCoord = + this.getTileCoordForTileUrlFunction(tileCoord, projection); + var tile = new ol.reproj.Tile( + sourceProjection, sourceTileGrid, + projection, targetTileGrid, + tileCoord, wrappedTileCoord, this.getTilePixelRatio(pixelRatio), + this.getGutterInternal(), + function(z, x, y, pixelRatio) { + return this.getTileInternal(z, x, y, pixelRatio, sourceProjection); + }.bind(this), this.reprojectionErrorThreshold_, + this.renderReprojectionEdges_); + + cache.set(tileCoordKey, tile); + return tile; + } + } +}; + + +/** + * @param {number} z Tile coordinate z. + * @param {number} x Tile coordinate x. + * @param {number} y Tile coordinate y. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {!ol.Tile} Tile. + * @protected + */ +ol.source.TileImage.prototype.getTileInternal = function(z, x, y, pixelRatio, projection) { + var /** @type {ol.Tile} */ tile = null; + var tileCoordKey = this.getKeyZXY(z, x, y); + var key = this.getKey(); + if (!this.tileCache.containsKey(tileCoordKey)) { + goog.asserts.assert(projection, 'argument projection is truthy'); + tile = this.createTile_(z, x, y, pixelRatio, projection, key); + this.tileCache.set(tileCoordKey, tile); + } else { + tile = /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); + if (tile.key != key) { + // The source's params changed. If the tile has an interim tile and if we + // can use it then we use it. Otherwise we create a new tile. In both + // cases we attempt to assign an interim tile to the new tile. + var /** @type {ol.Tile} */ interimTile = tile; + if (tile.interimTile && tile.interimTile.key == key) { + goog.asserts.assert(tile.interimTile.getState() == ol.TileState.LOADED); + goog.asserts.assert(tile.interimTile.interimTile === null); + tile = tile.interimTile; + if (interimTile.getState() == ol.TileState.LOADED) { + tile.interimTile = interimTile; + } + } else { + tile = this.createTile_(z, x, y, pixelRatio, projection, key); + if (interimTile.getState() == ol.TileState.LOADED) { + tile.interimTile = interimTile; + } else if (interimTile.interimTile && + interimTile.interimTile.getState() == ol.TileState.LOADED) { + tile.interimTile = interimTile.interimTile; + interimTile.interimTile = null; + } + } + if (tile.interimTile) { + tile.interimTile.interimTile = null; + } + this.tileCache.replace(tileCoordKey, tile); + } + } + goog.asserts.assert(tile); + return tile; +}; + + +/** + * Sets whether to render reprojection edges or not (usually for debugging). + * @param {boolean} render Render the edges. + * @api + */ +ol.source.TileImage.prototype.setRenderReprojectionEdges = function(render) { + if (!ol.ENABLE_RASTER_REPROJECTION || + this.renderReprojectionEdges_ == render) { + return; + } + this.renderReprojectionEdges_ = render; + for (var id in this.tileCacheForProjection) { + this.tileCacheForProjection[id].clear(); + } + this.changed(); +}; + + +/** + * Sets the tile grid to use when reprojecting the tiles to the given + * projection instead of the default tile grid for the projection. + * + * This can be useful when the default tile grid cannot be created + * (e.g. projection has no extent defined) or + * for optimization reasons (custom tile size, resolutions, ...). + * + * @param {ol.ProjectionLike} projection Projection. + * @param {ol.tilegrid.TileGrid} tilegrid Tile grid to use for the projection. + * @api + */ +ol.source.TileImage.prototype.setTileGridForProjection = function(projection, tilegrid) { + if (ol.ENABLE_RASTER_REPROJECTION) { + var proj = ol.proj.get(projection); + if (proj) { + var projKey = goog.getUid(proj).toString(); + if (!(projKey in this.tileGridForProjection)) { + this.tileGridForProjection[projKey] = tilegrid; + } + } + } +}; + + +/** + * @param {ol.ImageTile} imageTile Image tile. + * @param {string} src Source. + */ +ol.source.TileImage.defaultTileLoadFunction = function(imageTile, src) { + imageTile.getImage().src = src; +}; + +goog.provide('ol.source.BingMaps'); + +goog.require('goog.asserts'); +goog.require('ol.Attribution'); +goog.require('ol.TileRange'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.extent'); +goog.require('ol.net'); +goog.require('ol.proj'); +goog.require('ol.source.State'); +goog.require('ol.source.TileImage'); +goog.require('ol.tilecoord'); + + +/** + * @classdesc + * Layer source for Bing Maps tile data. + * + * @constructor + * @extends {ol.source.TileImage} + * @param {olx.source.BingMapsOptions} options Bing Maps options. + * @api stable + */ +ol.source.BingMaps = function(options) { + + ol.source.TileImage.call(this, { + cacheSize: options.cacheSize, + crossOrigin: 'anonymous', + opaque: true, + projection: ol.proj.get('EPSG:3857'), + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + state: ol.source.State.LOADING, + tileLoadFunction: options.tileLoadFunction, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); + + /** + * @private + * @type {string} + */ + this.culture_ = options.culture !== undefined ? options.culture : 'en-us'; + + /** + * @private + * @type {number} + */ + this.maxZoom_ = options.maxZoom !== undefined ? options.maxZoom : -1; + + var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/' + + options.imagerySet + + '?uriScheme=https&include=ImageryProviders&key=' + options.key; + + ol.net.jsonp(url, this.handleImageryMetadataResponse.bind(this), undefined, + 'jsonp'); + +}; +ol.inherits(ol.source.BingMaps, ol.source.TileImage); + + +/** + * The attribution containing a link to the Microsoft® Bingâ„¢ Maps Platform APIs’ + * Terms Of Use. + * @const + * @type {ol.Attribution} + * @api + */ +ol.source.BingMaps.TOS_ATTRIBUTION = new ol.Attribution({ + html: '<a class="ol-attribution-bing-tos" ' + + 'href="http://www.microsoft.com/maps/product/terms.html">' + + 'Terms of Use</a>' +}); + + +/** + * @param {BingMapsImageryMetadataResponse} response Response. + */ +ol.source.BingMaps.prototype.handleImageryMetadataResponse = function(response) { + + if (response.statusCode != 200 || + response.statusDescription != 'OK' || + response.authenticationResultCode != 'ValidCredentials' || + response.resourceSets.length != 1 || + response.resourceSets[0].resources.length != 1) { + this.setState(ol.source.State.ERROR); + return; + } + + var brandLogoUri = response.brandLogoUri; + if (brandLogoUri.indexOf('https') == -1) { + brandLogoUri = brandLogoUri.replace('http', 'https'); + } + //var copyright = response.copyright; // FIXME do we need to display this? + var resource = response.resourceSets[0].resources[0]; + goog.asserts.assert(resource.imageWidth == resource.imageHeight, + 'resource has imageWidth equal to imageHeight, i.e. is square'); + var maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_; + + var sourceProjection = this.getProjection(); + var extent = ol.tilegrid.extentFromProjection(sourceProjection); + var tileSize = resource.imageWidth == resource.imageHeight ? + resource.imageWidth : [resource.imageWidth, resource.imageHeight]; + var tileGrid = ol.tilegrid.createXYZ({ + extent: extent, + minZoom: resource.zoomMin, + maxZoom: maxZoom, + tileSize: tileSize + }); + this.tileGrid = tileGrid; + + var culture = this.culture_; + this.tileUrlFunction = ol.TileUrlFunction.createFromTileUrlFunctions( + resource.imageUrlSubdomains.map(function(subdomain) { + var quadKeyTileCoord = [0, 0, 0]; + var imageUrl = resource.imageUrl + .replace('{subdomain}', subdomain) + .replace('{culture}', culture); + return ( + /** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + goog.asserts.assert(ol.proj.equivalent( + projection, sourceProjection), + 'projections are equivalent'); + if (!tileCoord) { + return undefined; + } else { + ol.tilecoord.createOrUpdate(tileCoord[0], tileCoord[1], + -tileCoord[2] - 1, quadKeyTileCoord); + return imageUrl.replace('{quadkey}', ol.tilecoord.quadKey( + quadKeyTileCoord)); + } + }); + })); + + if (resource.imageryProviders) { + var transform = ol.proj.getTransformFromProjections( + ol.proj.get('EPSG:4326'), this.getProjection()); + + var attributions = resource.imageryProviders.map(function(imageryProvider) { + var html = imageryProvider.attribution; + /** @type {Object.<string, Array.<ol.TileRange>>} */ + var tileRanges = {}; + imageryProvider.coverageAreas.forEach(function(coverageArea) { + var minZ = coverageArea.zoomMin; + var maxZ = Math.min(coverageArea.zoomMax, maxZoom); + var bbox = coverageArea.bbox; + var epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]]; + var extent = ol.extent.applyTransform(epsg4326Extent, transform); + var tileRange, z, zKey; + for (z = minZ; z <= maxZ; ++z) { + zKey = z.toString(); + tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); + if (zKey in tileRanges) { + tileRanges[zKey].push(tileRange); + } else { + tileRanges[zKey] = [tileRange]; + } + } + }); + return new ol.Attribution({html: html, tileRanges: tileRanges}); + }); + attributions.push(ol.source.BingMaps.TOS_ATTRIBUTION); + this.setAttributions(attributions); + } + + this.setLogo(brandLogoUri); + + this.setState(ol.source.State.READY); + +}; + +goog.provide('ol.source.XYZ'); + +goog.require('ol.source.TileImage'); + + +/** + * @classdesc + * Layer source for tile data with URLs in a set XYZ format that are + * defined in a URL template. By default, this follows the widely-used + * Google grid where `x` 0 and `y` 0 are in the top left. Grids like + * TMS where `x` 0 and `y` 0 are in the bottom left can be used by + * using the `{-y}` placeholder in the URL template, so long as the + * source does not have a custom tile grid. In this case, + * {@link ol.source.TileImage} can be used with a `tileUrlFunction` + * such as: + * + * tileUrlFunction: function(coordinate) { + * return 'http://mapserver.com/' + coordinate[0] + '/' + + * coordinate[1] + '/' + coordinate[2] + '.png'; + * } + * + * + * @constructor + * @extends {ol.source.TileImage} + * @param {olx.source.XYZOptions=} opt_options XYZ options. + * @api stable + */ +ol.source.XYZ = function(opt_options) { + var options = opt_options || {}; + var projection = options.projection !== undefined ? + options.projection : 'EPSG:3857'; + + var tileGrid = options.tileGrid !== undefined ? options.tileGrid : + ol.tilegrid.createXYZ({ + extent: ol.tilegrid.extentFromProjection(projection), + maxZoom: options.maxZoom, + minZoom: options.minZoom, + tileSize: options.tileSize + }); + + ol.source.TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + logo: options.logo, + opaque: options.opaque, + projection: projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileGrid: tileGrid, + tileLoadFunction: options.tileLoadFunction, + tilePixelRatio: options.tilePixelRatio, + tileUrlFunction: options.tileUrlFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); + +}; +ol.inherits(ol.source.XYZ, ol.source.TileImage); + +goog.provide('ol.source.CartoDB'); + +goog.require('ol.object'); +goog.require('ol.source.State'); +goog.require('ol.source.XYZ'); + + +/** + * @classdesc + * Layer source for the CartoDB tiles. + * + * @constructor + * @extends {ol.source.XYZ} + * @param {olx.source.CartoDBOptions} options CartoDB options. + * @api + */ +ol.source.CartoDB = function(options) { + + /** + * @type {string} + * @private + */ + this.account_ = options.account; + + /** + * @type {string} + * @private + */ + this.mapId_ = options.map || ''; + + /** + * @type {!Object} + * @private + */ + this.config_ = options.config || {}; + + /** + * @type {!Object.<string, CartoDBLayerInfo>} + * @private + */ + this.templateCache_ = {}; + + ol.source.XYZ.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + logo: options.logo, + maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18, + minZoom: options.minZoom, + projection: options.projection, + state: ol.source.State.LOADING, + wrapX: options.wrapX + }); + this.initializeMap_(); +}; +ol.inherits(ol.source.CartoDB, ol.source.XYZ); + + +/** + * Returns the current config. + * @return {!Object} The current configuration. + * @api + */ +ol.source.CartoDB.prototype.getConfig = function() { + return this.config_; +}; + + +/** + * Updates the carto db config. + * @param {Object} config a key-value lookup. Values will replace current values + * in the config. + * @api + */ +ol.source.CartoDB.prototype.updateConfig = function(config) { + ol.object.assign(this.config_, config); + this.initializeMap_(); +}; + + +/** + * Sets the CartoDB config + * @param {Object} config In the case of anonymous maps, a CartoDB configuration + * object. + * If using named maps, a key-value lookup with the template parameters. + * @api + */ +ol.source.CartoDB.prototype.setConfig = function(config) { + this.config_ = config || {}; + this.initializeMap_(); +}; + + +/** + * Issue a request to initialize the CartoDB map. + * @private + */ +ol.source.CartoDB.prototype.initializeMap_ = function() { + var paramHash = JSON.stringify(this.config_); + if (this.templateCache_[paramHash]) { + this.applyTemplate_(this.templateCache_[paramHash]); + return; + } + var mapUrl = 'https://' + this.account_ + '.cartodb.com/api/v1/map'; + + if (this.mapId_) { + mapUrl += '/named/' + this.mapId_; + } + + var client = new XMLHttpRequest(); + client.addEventListener('load', this.handleInitResponse_.bind(this, paramHash)); + client.addEventListener('error', this.handleInitError_.bind(this)); + client.open('POST', mapUrl); + client.setRequestHeader('Content-type', 'application/json'); + client.send(JSON.stringify(this.config_)); +}; + + +/** + * Handle map initialization response. + * @param {string} paramHash a hash representing the parameter set that was used + * for the request + * @param {Event} event Event. + * @private + */ +ol.source.CartoDB.prototype.handleInitResponse_ = function(paramHash, event) { + var client = /** @type {XMLHttpRequest} */ (event.target); + if (client.status >= 200 && client.status < 300) { + var response; + try { + response = /** @type {CartoDBLayerInfo} */(JSON.parse(client.responseText)); + } catch (err) { + this.setState(ol.source.State.ERROR); + return; + } + this.applyTemplate_(response); + this.templateCache_[paramHash] = response; + this.setState(ol.source.State.READY); + } else { + this.setState(ol.source.State.ERROR); + } +}; + + +/** + * @private + * @param {Event} event Event. + */ +ol.source.CartoDB.prototype.handleInitError_ = function(event) { + this.setState(ol.source.State.ERROR); +}; + + +/** + * Apply the new tile urls returned by carto db + * @param {CartoDBLayerInfo} data Result of carto db call. + * @private + */ +ol.source.CartoDB.prototype.applyTemplate_ = function(data) { + var tilesUrl = 'https://' + data.cdn_url.https + '/' + this.account_ + + '/api/v1/map/' + data.layergroupid + '/{z}/{x}/{y}.png'; + this.setUrl(tilesUrl); +}; + +// FIXME keep cluster cache by resolution ? +// FIXME distance not respected because of the centroid + +goog.provide('ol.source.Cluster'); + +goog.require('goog.asserts'); +goog.require('ol.Feature'); +goog.require('ol.coordinate'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.geom.Point'); +goog.require('ol.source.Vector'); + + +/** + * @classdesc + * Layer source to cluster vector data. Works out of the box with point + * geometries. For other geometry types, or if not all geometries should be + * considered for clustering, a custom `geometryFunction` can be defined. + * + * @constructor + * @param {olx.source.ClusterOptions} options Constructor options. + * @extends {ol.source.Vector} + * @api + */ +ol.source.Cluster = function(options) { + ol.source.Vector.call(this, { + attributions: options.attributions, + extent: options.extent, + logo: options.logo, + projection: options.projection, + wrapX: options.wrapX + }); + + /** + * @type {number|undefined} + * @private + */ + this.resolution_ = undefined; + + /** + * @type {number} + * @private + */ + this.distance_ = options.distance !== undefined ? options.distance : 20; + + /** + * @type {Array.<ol.Feature>} + * @private + */ + this.features_ = []; + + /** + * @param {ol.Feature} feature Feature. + * @return {ol.geom.Point} Cluster calculation point. + */ + this.geometryFunction_ = options.geometryFunction || function(feature) { + var geometry = feature.getGeometry(); + goog.asserts.assert(geometry instanceof ol.geom.Point, + 'feature geometry is a ol.geom.Point instance'); + return geometry; + }; + + /** + * @type {ol.source.Vector} + * @private + */ + this.source_ = options.source; + + this.source_.on(ol.events.EventType.CHANGE, + ol.source.Cluster.prototype.onSourceChange_, this); +}; +ol.inherits(ol.source.Cluster, ol.source.Vector); + + +/** + * Get a reference to the wrapped source. + * @return {ol.source.Vector} Source. + * @api + */ +ol.source.Cluster.prototype.getSource = function() { + return this.source_; +}; + + +/** + * @inheritDoc + */ +ol.source.Cluster.prototype.loadFeatures = function(extent, resolution, + projection) { + this.source_.loadFeatures(extent, resolution, projection); + if (resolution !== this.resolution_) { + this.clear(); + this.resolution_ = resolution; + this.cluster_(); + this.addFeatures(this.features_); + } +}; + + +/** + * handle the source changing + * @private + */ +ol.source.Cluster.prototype.onSourceChange_ = function() { + this.clear(); + this.cluster_(); + this.addFeatures(this.features_); + this.changed(); +}; + + +/** + * @private + */ +ol.source.Cluster.prototype.cluster_ = function() { + if (this.resolution_ === undefined) { + return; + } + this.features_.length = 0; + var extent = ol.extent.createEmpty(); + var mapDistance = this.distance_ * this.resolution_; + var features = this.source_.getFeatures(); + + /** + * @type {!Object.<string, boolean>} + */ + var clustered = {}; + + for (var i = 0, ii = features.length; i < ii; i++) { + var feature = features[i]; + if (!(goog.getUid(feature).toString() in clustered)) { + var geometry = this.geometryFunction_(feature); + if (geometry) { + var coordinates = geometry.getCoordinates(); + ol.extent.createOrUpdateFromCoordinate(coordinates, extent); + ol.extent.buffer(extent, mapDistance, extent); + + var neighbors = this.source_.getFeaturesInExtent(extent); + goog.asserts.assert(neighbors.length >= 1, 'at least one neighbor found'); + neighbors = neighbors.filter(function(neighbor) { + var uid = goog.getUid(neighbor).toString(); + if (!(uid in clustered)) { + clustered[uid] = true; + return true; + } else { + return false; + } + }); + this.features_.push(this.createCluster_(neighbors)); + } + } + } + goog.asserts.assert( + Object.keys(clustered).length == this.source_.getFeatures().length, + 'number of clustered equals number of features in the source'); +}; + + +/** + * @param {Array.<ol.Feature>} features Features + * @return {ol.Feature} The cluster feature. + * @private + */ +ol.source.Cluster.prototype.createCluster_ = function(features) { + var centroid = [0, 0]; + for (var i = features.length - 1; i >= 0; --i) { + var geometry = this.geometryFunction_(features[i]); + if (geometry) { + ol.coordinate.add(centroid, geometry.getCoordinates()); + } else { + features.splice(i, 1); + } + } + ol.coordinate.scale(centroid, 1 / features.length); + + var cluster = new ol.Feature(new ol.geom.Point(centroid)); + cluster.set('features', features); + return cluster; +}; + +goog.provide('ol.uri'); + + +/** + * Appends query parameters to a URI. + * + * @param {string} uri The original URI, which may already have query data. + * @param {!Object} params An object where keys are URI-encoded parameter keys, + * and the values are arbitrary types or arrays. + * @return {string} The new URI. + */ +ol.uri.appendParams = function(uri, params) { + var qs = Object.keys(params).map(function(k) { + return k + '=' + encodeURIComponent(params[k]); + }).join('&'); + // remove any trailing ? or & + uri = uri.replace(/[?&]$/, ''); + // append ? or & depending on whether uri has existing parameters + uri = uri.indexOf('?') === -1 ? uri + '?' : uri + '&'; + return uri + qs; +}; + +goog.provide('ol.source.ImageArcGISRest'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.Image'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.source.Image'); +goog.require('ol.uri'); + + +/** + * @classdesc + * Source for data from ArcGIS Rest services providing single, untiled images. + * Useful when underlying map service has labels. + * + * If underlying map service is not using labels, + * take advantage of ol image caching and use + * {@link ol.source.TileArcGISRest} data source. + * + * @constructor + * @fires ol.source.ImageEvent + * @extends {ol.source.Image} + * @param {olx.source.ImageArcGISRestOptions=} opt_options Image ArcGIS Rest Options. + * @api + */ +ol.source.ImageArcGISRest = function(opt_options) { + + var options = opt_options || {}; + + ol.source.Image.call(this, { + attributions: options.attributions, + logo: options.logo, + projection: options.projection, + resolutions: options.resolutions + }); + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @private + * @type {string|undefined} + */ + this.url_ = options.url; + + /** + * @private + * @type {ol.ImageLoadFunctionType} + */ + this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? + options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; + + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {ol.Image} + */ + this.image_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.imageSize_ = [0, 0]; + + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; + +}; +ol.inherits(ol.source.ImageArcGISRest, ol.source.Image); + + +/** + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api stable + */ +ol.source.ImageArcGISRest.prototype.getParams = function() { + return this.params_; +}; + + +/** + * @inheritDoc + */ +ol.source.ImageArcGISRest.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { + + if (this.url_ === undefined) { + return null; + } + + resolution = this.findNearestResolution(resolution); + + var image = this.image_; + if (image && + this.renderedRevision_ == this.getRevision() && + image.getResolution() == resolution && + image.getPixelRatio() == pixelRatio && + ol.extent.containsExtent(image.getExtent(), extent)) { + return image; + } + + var params = { + 'F': 'image', + 'FORMAT': 'PNG32', + 'TRANSPARENT': true + }; + ol.object.assign(params, this.params_); + + extent = extent.slice(); + var centerX = (extent[0] + extent[2]) / 2; + var centerY = (extent[1] + extent[3]) / 2; + if (this.ratio_ != 1) { + var halfWidth = this.ratio_ * ol.extent.getWidth(extent) / 2; + var halfHeight = this.ratio_ * ol.extent.getHeight(extent) / 2; + extent[0] = centerX - halfWidth; + extent[1] = centerY - halfHeight; + extent[2] = centerX + halfWidth; + extent[3] = centerY + halfHeight; + } + + var imageResolution = resolution / pixelRatio; + + // Compute an integer width and height. + var width = Math.ceil(ol.extent.getWidth(extent) / imageResolution); + var height = Math.ceil(ol.extent.getHeight(extent) / imageResolution); + + // Modify the extent to match the integer width and height. + extent[0] = centerX - imageResolution * width / 2; + extent[2] = centerX + imageResolution * width / 2; + extent[1] = centerY - imageResolution * height / 2; + extent[3] = centerY + imageResolution * height / 2; + + this.imageSize_[0] = width; + this.imageSize_[1] = height; + + var url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio, + projection, params); + + this.image_ = new ol.Image(extent, resolution, pixelRatio, + this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_); + + this.renderedRevision_ = this.getRevision(); + + ol.events.listen(this.image_, ol.events.EventType.CHANGE, + this.handleImageChange, this); + + return this.image_; + +}; + + +/** + * Return the image load function of the source. + * @return {ol.ImageLoadFunctionType} The image load function. + * @api + */ +ol.source.ImageArcGISRest.prototype.getImageLoadFunction = function() { + return this.imageLoadFunction_; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @param {Object} params Params. + * @return {string} Request URL. + * @private + */ +ol.source.ImageArcGISRest.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) { + + goog.asserts.assert(this.url_ !== undefined, 'url is defined'); + + // ArcGIS Server only wants the numeric portion of the projection ID. + var srid = projection.getCode().split(':').pop(); + + params['SIZE'] = size[0] + ',' + size[1]; + params['BBOX'] = extent.join(','); + params['BBOXSR'] = srid; + params['IMAGESR'] = srid; + params['DPI'] = 90 * pixelRatio; + + var url = this.url_; + + var modifiedUrl = url + .replace(/MapServer\/?$/, 'MapServer/export') + .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); + if (modifiedUrl == url) { + goog.asserts.fail('Unknown Rest Service', url); + } + return ol.uri.appendParams(modifiedUrl, params); +}; + + +/** + * Return the URL used for this ArcGIS source. + * @return {string|undefined} URL. + * @api stable + */ +ol.source.ImageArcGISRest.prototype.getUrl = function() { + return this.url_; +}; + + +/** + * Set the image load function of the source. + * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. + * @api + */ +ol.source.ImageArcGISRest.prototype.setImageLoadFunction = function(imageLoadFunction) { + this.image_ = null; + this.imageLoadFunction_ = imageLoadFunction; + this.changed(); +}; + + +/** + * Set the URL to use for requests. + * @param {string|undefined} url URL. + * @api stable + */ +ol.source.ImageArcGISRest.prototype.setUrl = function(url) { + if (url != this.url_) { + this.url_ = url; + this.image_ = null; + this.changed(); + } +}; + + +/** + * Update the user-provided params. + * @param {Object} params Params. + * @api stable + */ +ol.source.ImageArcGISRest.prototype.updateParams = function(params) { + ol.object.assign(this.params_, params); + this.image_ = null; + this.changed(); +}; + +goog.provide('ol.source.ImageMapGuide'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.Image'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.source.Image'); +goog.require('ol.uri'); + + +/** + * @classdesc + * Source for images from Mapguide servers + * + * @constructor + * @fires ol.source.ImageEvent + * @extends {ol.source.Image} + * @param {olx.source.ImageMapGuideOptions} options Options. + * @api stable + */ +ol.source.ImageMapGuide = function(options) { + + ol.source.Image.call(this, { + projection: options.projection, + resolutions: options.resolutions + }); + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @private + * @type {number} + */ + this.displayDpi_ = options.displayDpi !== undefined ? + options.displayDpi : 96; + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {string|undefined} + */ + this.url_ = options.url; + + /** + * @private + * @type {ol.ImageLoadFunctionType} + */ + this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? + options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + + /** + * @private + * @type {number} + */ + this.metersPerUnit_ = options.metersPerUnit !== undefined ? + options.metersPerUnit : 1; + + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? options.ratio : 1; + + /** + * @private + * @type {boolean} + */ + this.useOverlay_ = options.useOverlay !== undefined ? + options.useOverlay : false; + + /** + * @private + * @type {ol.Image} + */ + this.image_ = null; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + +}; +ol.inherits(ol.source.ImageMapGuide, ol.source.Image); + + +/** + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api stable + */ +ol.source.ImageMapGuide.prototype.getParams = function() { + return this.params_; +}; + + +/** + * @inheritDoc + */ +ol.source.ImageMapGuide.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { + resolution = this.findNearestResolution(resolution); + pixelRatio = this.hidpi_ ? pixelRatio : 1; + + var image = this.image_; + if (image && + this.renderedRevision_ == this.getRevision() && + image.getResolution() == resolution && + image.getPixelRatio() == pixelRatio && + ol.extent.containsExtent(image.getExtent(), extent)) { + return image; + } + + if (this.ratio_ != 1) { + extent = extent.slice(); + ol.extent.scaleFromCenter(extent, this.ratio_); + } + var width = ol.extent.getWidth(extent) / resolution; + var height = ol.extent.getHeight(extent) / resolution; + var size = [width * pixelRatio, height * pixelRatio]; + + if (this.url_ !== undefined) { + var imageUrl = this.getUrl(this.url_, this.params_, extent, size, + projection); + image = new ol.Image(extent, resolution, pixelRatio, + this.getAttributions(), imageUrl, this.crossOrigin_, + this.imageLoadFunction_); + ol.events.listen(image, ol.events.EventType.CHANGE, + this.handleImageChange, this); + } else { + image = null; + } + this.image_ = image; + this.renderedRevision_ = this.getRevision(); + + return image; +}; + + +/** + * Return the image load function of the source. + * @return {ol.ImageLoadFunctionType} The image load function. + * @api + */ +ol.source.ImageMapGuide.prototype.getImageLoadFunction = function() { + return this.imageLoadFunction_; +}; + + +/** + * @param {ol.Extent} extent The map extents. + * @param {ol.Size} size The viewport size. + * @param {number} metersPerUnit The meters-per-unit value. + * @param {number} dpi The display resolution. + * @return {number} The computed map scale. + */ +ol.source.ImageMapGuide.getScale = function(extent, size, metersPerUnit, dpi) { + var mcsW = ol.extent.getWidth(extent); + var mcsH = ol.extent.getHeight(extent); + var devW = size[0]; + var devH = size[1]; + var mpp = 0.0254 / dpi; + if (devH * mcsW > devW * mcsH) { + return mcsW * metersPerUnit / (devW * mpp); // width limited + } else { + return mcsH * metersPerUnit / (devH * mpp); // height limited + } +}; + + +/** + * Update the user-provided params. + * @param {Object} params Params. + * @api stable + */ +ol.source.ImageMapGuide.prototype.updateParams = function(params) { + ol.object.assign(this.params_, params); + this.changed(); +}; + + +/** + * @param {string} baseUrl The mapagent url. + * @param {Object.<string, string|number>} params Request parameters. + * @param {ol.Extent} extent Extent. + * @param {ol.Size} size Size. + * @param {ol.proj.Projection} projection Projection. + * @return {string} The mapagent map image request URL. + */ +ol.source.ImageMapGuide.prototype.getUrl = function(baseUrl, params, extent, size, projection) { + var scale = ol.source.ImageMapGuide.getScale(extent, size, + this.metersPerUnit_, this.displayDpi_); + var center = ol.extent.getCenter(extent); + var baseParams = { + 'OPERATION': this.useOverlay_ ? 'GETDYNAMICMAPOVERLAYIMAGE' : 'GETMAPIMAGE', + 'VERSION': '2.0.0', + 'LOCALE': 'en', + 'CLIENTAGENT': 'ol.source.ImageMapGuide source', + 'CLIP': '1', + 'SETDISPLAYDPI': this.displayDpi_, + 'SETDISPLAYWIDTH': Math.round(size[0]), + 'SETDISPLAYHEIGHT': Math.round(size[1]), + 'SETVIEWSCALE': scale, + 'SETVIEWCENTERX': center[0], + 'SETVIEWCENTERY': center[1] + }; + ol.object.assign(baseParams, params); + return ol.uri.appendParams(baseUrl, baseParams); +}; + + +/** + * Set the image load function of the MapGuide source. + * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. + * @api + */ +ol.source.ImageMapGuide.prototype.setImageLoadFunction = function( + imageLoadFunction) { + this.image_ = null; + this.imageLoadFunction_ = imageLoadFunction; + this.changed(); +}; + +goog.provide('ol.source.ImageStatic'); + +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.Image'); +goog.require('ol.ImageState'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.proj'); +goog.require('ol.source.Image'); + + +/** + * @classdesc + * A layer source for displaying a single, static image. + * + * @constructor + * @extends {ol.source.Image} + * @param {olx.source.ImageStaticOptions} options Options. + * @api stable + */ +ol.source.ImageStatic = function(options) { + var imageExtent = options.imageExtent; + + var crossOrigin = options.crossOrigin !== undefined ? + options.crossOrigin : null; + + var /** @type {ol.ImageLoadFunctionType} */ imageLoadFunction = + options.imageLoadFunction !== undefined ? + options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; + + ol.source.Image.call(this, { + attributions: options.attributions, + logo: options.logo, + projection: ol.proj.get(options.projection) + }); + + /** + * @private + * @type {ol.Image} + */ + this.image_ = new ol.Image(imageExtent, undefined, 1, this.getAttributions(), + options.url, crossOrigin, imageLoadFunction); + + /** + * @private + * @type {ol.Size} + */ + this.imageSize_ = options.imageSize ? options.imageSize : null; + + ol.events.listen(this.image_, ol.events.EventType.CHANGE, + this.handleImageChange, this); + +}; +ol.inherits(ol.source.ImageStatic, ol.source.Image); + + +/** + * @inheritDoc + */ +ol.source.ImageStatic.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { + if (ol.extent.intersects(extent, this.image_.getExtent())) { + return this.image_; + } + return null; +}; + + +/** + * @inheritDoc + */ +ol.source.ImageStatic.prototype.handleImageChange = function(evt) { + if (this.image_.getState() == ol.ImageState.LOADED) { + var imageExtent = this.image_.getExtent(); + var image = this.image_.getImage(); + var imageWidth, imageHeight; + if (this.imageSize_) { + imageWidth = this.imageSize_[0]; + imageHeight = this.imageSize_[1]; + } else { + // TODO: remove the type cast when a closure-compiler > 20160315 is used. + // see: https://github.com/google/closure-compiler/pull/1664 + imageWidth = /** @type {number} */ (image.width); + imageHeight = /** @type {number} */ (image.height); + } + var resolution = ol.extent.getHeight(imageExtent) / imageHeight; + var targetWidth = Math.ceil(ol.extent.getWidth(imageExtent) / resolution); + if (targetWidth != imageWidth) { + var context = ol.dom.createCanvasContext2D(targetWidth, imageHeight); + var canvas = context.canvas; + context.drawImage(image, 0, 0, imageWidth, imageHeight, + 0, 0, canvas.width, canvas.height); + this.image_.setImage(canvas); + } + } + ol.source.Image.prototype.handleImageChange.call(this, evt); +}; + +goog.provide('ol.source.wms'); +goog.provide('ol.source.wms.ServerType'); + + +/** + * Available server types: `'carmentaserver'`, `'geoserver'`, `'mapserver'`, + * `'qgis'`. These are servers that have vendor parameters beyond the WMS + * specification that OpenLayers can make use of. + * @enum {string} + */ +ol.source.wms.ServerType = { + CARMENTA_SERVER: 'carmentaserver', + GEOSERVER: 'geoserver', + MAPSERVER: 'mapserver', + QGIS: 'qgis' +}; + +// FIXME cannot be shared between maps with different projections + +goog.provide('ol.source.ImageWMS'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.Image'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.source.Image'); +goog.require('ol.source.wms'); +goog.require('ol.source.wms.ServerType'); +goog.require('ol.string'); +goog.require('ol.uri'); + + +/** + * @classdesc + * Source for WMS servers providing single, untiled images. + * + * @constructor + * @fires ol.source.ImageEvent + * @extends {ol.source.Image} + * @param {olx.source.ImageWMSOptions=} opt_options Options. + * @api stable + */ +ol.source.ImageWMS = function(opt_options) { + + var options = opt_options || {}; + + ol.source.Image.call(this, { + attributions: options.attributions, + logo: options.logo, + projection: options.projection, + resolutions: options.resolutions + }); + + /** + * @private + * @type {?string} + */ + this.crossOrigin_ = + options.crossOrigin !== undefined ? options.crossOrigin : null; + + /** + * @private + * @type {string|undefined} + */ + this.url_ = options.url; + + /** + * @private + * @type {ol.ImageLoadFunctionType} + */ + this.imageLoadFunction_ = options.imageLoadFunction !== undefined ? + options.imageLoadFunction : ol.source.Image.defaultImageLoadFunction; + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {boolean} + */ + this.v13_ = true; + this.updateV13_(); + + /** + * @private + * @type {ol.source.wms.ServerType|undefined} + */ + this.serverType_ = + /** @type {ol.source.wms.ServerType|undefined} */ (options.serverType); + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + + /** + * @private + * @type {ol.Image} + */ + this.image_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.imageSize_ = [0, 0]; + + /** + * @private + * @type {number} + */ + this.renderedRevision_ = 0; + + /** + * @private + * @type {number} + */ + this.ratio_ = options.ratio !== undefined ? options.ratio : 1.5; + +}; +ol.inherits(ol.source.ImageWMS, ol.source.Image); + + +/** + * @const + * @type {ol.Size} + * @private + */ +ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_ = [101, 101]; + + +/** + * Return the GetFeatureInfo URL for the passed coordinate, resolution, and + * projection. Return `undefined` if the GetFeatureInfo URL cannot be + * constructed. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {ol.ProjectionLike} projection Projection. + * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should + * be provided. If `QUERY_LAYERS` is not provided then the layers specified + * in the `LAYERS` parameter will be used. `VERSION` should not be + * specified here. + * @return {string|undefined} GetFeatureInfo URL. + * @api stable + */ +ol.source.ImageWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { + + goog.asserts.assert(!('VERSION' in params), + 'key VERSION is not allowed in params'); + + if (this.url_ === undefined) { + return undefined; + } + + var extent = ol.extent.getForViewAndSize( + coordinate, resolution, 0, + ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_); + + var baseParams = { + 'SERVICE': 'WMS', + 'VERSION': ol.DEFAULT_WMS_VERSION, + 'REQUEST': 'GetFeatureInfo', + 'FORMAT': 'image/png', + 'TRANSPARENT': true, + 'QUERY_LAYERS': this.params_['LAYERS'] + }; + ol.object.assign(baseParams, this.params_, params); + + var x = Math.floor((coordinate[0] - extent[0]) / resolution); + var y = Math.floor((extent[3] - coordinate[1]) / resolution); + baseParams[this.v13_ ? 'I' : 'X'] = x; + baseParams[this.v13_ ? 'J' : 'Y'] = y; + + return this.getRequestUrl_( + extent, ol.source.ImageWMS.GETFEATUREINFO_IMAGE_SIZE_, + 1, ol.proj.get(projection), baseParams); +}; + + +/** + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api stable + */ +ol.source.ImageWMS.prototype.getParams = function() { + return this.params_; +}; + + +/** + * @inheritDoc + */ +ol.source.ImageWMS.prototype.getImageInternal = function(extent, resolution, pixelRatio, projection) { + + if (this.url_ === undefined) { + return null; + } + + resolution = this.findNearestResolution(resolution); + + if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { + pixelRatio = 1; + } + + extent = extent.slice(); + var centerX = (extent[0] + extent[2]) / 2; + var centerY = (extent[1] + extent[3]) / 2; + + var imageResolution = resolution / pixelRatio; + var imageWidth = ol.extent.getWidth(extent) / imageResolution; + var imageHeight = ol.extent.getHeight(extent) / imageResolution; + + var image = this.image_; + if (image && + this.renderedRevision_ == this.getRevision() && + image.getResolution() == resolution && + image.getPixelRatio() == pixelRatio && + ol.extent.containsExtent(image.getExtent(), extent)) { + return image; + } + + if (this.ratio_ != 1) { + var halfWidth = this.ratio_ * ol.extent.getWidth(extent) / 2; + var halfHeight = this.ratio_ * ol.extent.getHeight(extent) / 2; + extent[0] = centerX - halfWidth; + extent[1] = centerY - halfHeight; + extent[2] = centerX + halfWidth; + extent[3] = centerY + halfHeight; + } + + var params = { + 'SERVICE': 'WMS', + 'VERSION': ol.DEFAULT_WMS_VERSION, + 'REQUEST': 'GetMap', + 'FORMAT': 'image/png', + 'TRANSPARENT': true + }; + ol.object.assign(params, this.params_); + + this.imageSize_[0] = Math.ceil(imageWidth * this.ratio_); + this.imageSize_[1] = Math.ceil(imageHeight * this.ratio_); + + var url = this.getRequestUrl_(extent, this.imageSize_, pixelRatio, + projection, params); + + this.image_ = new ol.Image(extent, resolution, pixelRatio, + this.getAttributions(), url, this.crossOrigin_, this.imageLoadFunction_); + + this.renderedRevision_ = this.getRevision(); + + ol.events.listen(this.image_, ol.events.EventType.CHANGE, + this.handleImageChange, this); + + return this.image_; + +}; + + +/** + * Return the image load function of the source. + * @return {ol.ImageLoadFunctionType} The image load function. + * @api + */ +ol.source.ImageWMS.prototype.getImageLoadFunction = function() { + return this.imageLoadFunction_; +}; + + +/** + * @param {ol.Extent} extent Extent. + * @param {ol.Size} size Size. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @param {Object} params Params. + * @return {string} Request URL. + * @private + */ +ol.source.ImageWMS.prototype.getRequestUrl_ = function(extent, size, pixelRatio, projection, params) { + + goog.asserts.assert(this.url_ !== undefined, 'url is defined'); + + params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); + + if (!('STYLES' in this.params_)) { + params['STYLES'] = ''; + } + + if (pixelRatio != 1) { + switch (this.serverType_) { + case ol.source.wms.ServerType.GEOSERVER: + var dpi = (90 * pixelRatio + 0.5) | 0; + if ('FORMAT_OPTIONS' in params) { + params['FORMAT_OPTIONS'] += ';dpi:' + dpi; + } else { + params['FORMAT_OPTIONS'] = 'dpi:' + dpi; + } + break; + case ol.source.wms.ServerType.MAPSERVER: + params['MAP_RESOLUTION'] = 90 * pixelRatio; + break; + case ol.source.wms.ServerType.CARMENTA_SERVER: + case ol.source.wms.ServerType.QGIS: + params['DPI'] = 90 * pixelRatio; + break; + default: + goog.asserts.fail('unknown serverType configured'); + break; + } + } + + params['WIDTH'] = size[0]; + params['HEIGHT'] = size[1]; + + var axisOrientation = projection.getAxisOrientation(); + var bbox; + if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { + bbox = [extent[1], extent[0], extent[3], extent[2]]; + } else { + bbox = extent; + } + params['BBOX'] = bbox.join(','); + + return ol.uri.appendParams(this.url_, params); +}; + + +/** + * Return the URL used for this WMS source. + * @return {string|undefined} URL. + * @api stable + */ +ol.source.ImageWMS.prototype.getUrl = function() { + return this.url_; +}; + + +/** + * Set the image load function of the source. + * @param {ol.ImageLoadFunctionType} imageLoadFunction Image load function. + * @api + */ +ol.source.ImageWMS.prototype.setImageLoadFunction = function( + imageLoadFunction) { + this.image_ = null; + this.imageLoadFunction_ = imageLoadFunction; + this.changed(); +}; + + +/** + * Set the URL to use for requests. + * @param {string|undefined} url URL. + * @api stable + */ +ol.source.ImageWMS.prototype.setUrl = function(url) { + if (url != this.url_) { + this.url_ = url; + this.image_ = null; + this.changed(); + } +}; + + +/** + * Update the user-provided params. + * @param {Object} params Params. + * @api stable + */ +ol.source.ImageWMS.prototype.updateParams = function(params) { + ol.object.assign(this.params_, params); + this.updateV13_(); + this.image_ = null; + this.changed(); +}; + + +/** + * @private + */ +ol.source.ImageWMS.prototype.updateV13_ = function() { + var version = this.params_['VERSION'] || ol.DEFAULT_WMS_VERSION; + this.v13_ = ol.string.compareVersions(version, '1.3') >= 0; +}; + +goog.provide('ol.source.OSM'); + +goog.require('ol.Attribution'); +goog.require('ol.source.XYZ'); + + +/** + * @classdesc + * Layer source for the OpenStreetMap tile server. + * + * @constructor + * @extends {ol.source.XYZ} + * @param {olx.source.OSMOptions=} opt_options Open Street Map options. + * @api stable + */ +ol.source.OSM = function(opt_options) { + + var options = opt_options || {}; + + var attributions; + if (options.attributions !== undefined) { + attributions = options.attributions; + } else { + attributions = [ol.source.OSM.ATTRIBUTION]; + } + + var crossOrigin = options.crossOrigin !== undefined ? + options.crossOrigin : 'anonymous'; + + var url = options.url !== undefined ? + options.url : 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'; + + ol.source.XYZ.call(this, { + attributions: attributions, + cacheSize: options.cacheSize, + crossOrigin: crossOrigin, + opaque: options.opaque !== undefined ? options.opaque : true, + maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileLoadFunction: options.tileLoadFunction, + url: url, + wrapX: options.wrapX + }); + +}; +ol.inherits(ol.source.OSM, ol.source.XYZ); + + +/** + * The attribution containing a link to the OpenStreetMap Copyright and License + * page. + * @const + * @type {ol.Attribution} + * @api + */ +ol.source.OSM.ATTRIBUTION = new ol.Attribution({ + html: '© ' + + '<a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> ' + + 'contributors.' +}); + +goog.provide('ol.ext.pixelworks'); +/** @typedef {function(*)} */ +ol.ext.pixelworks; +(function() { +var exports = {}; +var module = {exports: exports}; +var define; +/** + * @fileoverview + * @suppress {accessControls, ambiguousFunctionDecl, checkDebuggerStatement, checkRegExp, checkTypes, checkVars, const, constantProperty, deprecated, duplicate, es5Strict, fileoverviewTags, missingProperties, nonStandardJsDocs, strictModuleDepCheck, suspiciousCode, undefinedNames, undefinedVars, unknownDefines, uselessCode, visibility} + */ +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.pixelworks = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ +var Processor = _dereq_('./processor'); + +exports.Processor = Processor; + +},{"./processor":2}],2:[function(_dereq_,module,exports){ +var newImageData = _dereq_('./util').newImageData; + +/** + * Create a function for running operations. This function is serialized for + * use in a worker. + * @param {function(Array, Object):*} operation The operation. + * @return {function(Object):ArrayBuffer} A function that takes an object with + * buffers, meta, imageOps, width, and height properties and returns an array + * buffer. + */ +function createMinion(operation) { + var workerHasImageData = true; + try { + new ImageData(10, 10); + } catch (_) { + workerHasImageData = false; + } + + function newWorkerImageData(data, width, height) { + if (workerHasImageData) { + return new ImageData(data, width, height); + } else { + return {data: data, width: width, height: height}; + } + } + + return function(data) { + // bracket notation for minification support + var buffers = data['buffers']; + var meta = data['meta']; + var imageOps = data['imageOps']; + var width = data['width']; + var height = data['height']; + + var numBuffers = buffers.length; + var numBytes = buffers[0].byteLength; + var output, b; + + if (imageOps) { + var images = new Array(numBuffers); + for (b = 0; b < numBuffers; ++b) { + images[b] = newWorkerImageData( + new Uint8ClampedArray(buffers[b]), width, height); + } + output = operation(images, meta).data; + } else { + output = new Uint8ClampedArray(numBytes); + var arrays = new Array(numBuffers); + var pixels = new Array(numBuffers); + for (b = 0; b < numBuffers; ++b) { + arrays[b] = new Uint8ClampedArray(buffers[b]); + pixels[b] = [0, 0, 0, 0]; + } + for (var i = 0; i < numBytes; i += 4) { + for (var j = 0; j < numBuffers; ++j) { + var array = arrays[j]; + pixels[j][0] = array[i]; + pixels[j][1] = array[i + 1]; + pixels[j][2] = array[i + 2]; + pixels[j][3] = array[i + 3]; + } + var pixel = operation(pixels, meta); + output[i] = pixel[0]; + output[i + 1] = pixel[1]; + output[i + 2] = pixel[2]; + output[i + 3] = pixel[3]; + } + } + return output.buffer; + }; +} + +/** + * Create a worker for running operations. + * @param {Object} config Configuration. + * @param {function(MessageEvent)} onMessage Called with a message event. + * @return {Worker} The worker. + */ +function createWorker(config, onMessage) { + var lib = Object.keys(config.lib || {}).map(function(name) { + return 'var ' + name + ' = ' + config.lib[name].toString() + ';'; + }); + + var lines = lib.concat([ + 'var __minion__ = (' + createMinion.toString() + ')(', config.operation.toString(), ');', + 'self.addEventListener("message", function(event) {', + ' var buffer = __minion__(event.data);', + ' self.postMessage({buffer: buffer, meta: event.data.meta}, [buffer]);', + '});' + ]); + + var blob = new Blob(lines, {type: 'text/javascript'}); + var source = URL.createObjectURL(blob); + var worker = new Worker(source); + worker.addEventListener('message', onMessage); + return worker; +} + +/** + * Create a faux worker for running operations. + * @param {Object} config Configuration. + * @param {function(MessageEvent)} onMessage Called with a message event. + * @return {Object} The faux worker. + */ +function createFauxWorker(config, onMessage) { + var minion = createMinion(config.operation); + return { + postMessage: function(data) { + setTimeout(function() { + onMessage({'data': {'buffer': minion(data), 'meta': data['meta']}}); + }, 0); + } + }; +} + +/** + * A processor runs pixel or image operations in workers. + * @param {Object} config Configuration. + */ +function Processor(config) { + this._imageOps = !!config.imageOps; + var threads; + if (config.threads === 0) { + threads = 0; + } else if (this._imageOps) { + threads = 1; + } else { + threads = config.threads || 1; + } + var workers = []; + if (threads) { + for (var i = 0; i < threads; ++i) { + workers[i] = createWorker(config, this._onWorkerMessage.bind(this, i)); + } + } else { + workers[0] = createFauxWorker(config, this._onWorkerMessage.bind(this, 0)); + } + this._workers = workers; + this._queue = []; + this._maxQueueLength = config.queue || Infinity; + this._running = 0; + this._dataLookup = {}; + this._job = null; +} + +/** + * Run operation on input data. + * @param {Array.<Array|ImageData>} inputs Array of pixels or image data + * (depending on the operation type). + * @param {Object} meta A user data object. This is passed to all operations + * and must be serializable. + * @param {function(Error, ImageData, Object)} callback Called when work + * completes. The first argument is any error. The second is the ImageData + * generated by operations. The third is the user data object. + */ +Processor.prototype.process = function(inputs, meta, callback) { + this._enqueue({ + inputs: inputs, + meta: meta, + callback: callback + }); + this._dispatch(); +}; + +/** + * Stop responding to any completed work and destroy the processor. + */ +Processor.prototype.destroy = function() { + for (var key in this) { + this[key] = null; + } + this._destroyed = true; +}; + +/** + * Add a job to the queue. + * @param {Object} job The job. + */ +Processor.prototype._enqueue = function(job) { + this._queue.push(job); + while (this._queue.length > this._maxQueueLength) { + this._queue.shift().callback(null, null); + } +}; + +/** + * Dispatch a job. + */ +Processor.prototype._dispatch = function() { + if (this._running === 0 && this._queue.length > 0) { + var job = this._job = this._queue.shift(); + var width = job.inputs[0].width; + var height = job.inputs[0].height; + var buffers = job.inputs.map(function(input) { + return input.data.buffer; + }); + var threads = this._workers.length; + this._running = threads; + if (threads === 1) { + this._workers[0].postMessage({ + 'buffers': buffers, + 'meta': job.meta, + 'imageOps': this._imageOps, + 'width': width, + 'height': height + }, buffers); + } else { + var length = job.inputs[0].data.length; + var segmentLength = 4 * Math.ceil(length / 4 / threads); + for (var i = 0; i < threads; ++i) { + var offset = i * segmentLength; + var slices = []; + for (var j = 0, jj = buffers.length; j < jj; ++j) { + slices.push(buffers[i].slice(offset, offset + segmentLength)); + } + this._workers[i].postMessage({ + 'buffers': slices, + 'meta': job.meta, + 'imageOps': this._imageOps, + 'width': width, + 'height': height + }, slices); + } + } + } +}; + +/** + * Handle messages from the worker. + * @param {number} index The worker index. + * @param {MessageEvent} event The message event. + */ +Processor.prototype._onWorkerMessage = function(index, event) { + if (this._destroyed) { + return; + } + this._dataLookup[index] = event.data; + --this._running; + if (this._running === 0) { + this._resolveJob(); + } +}; + +/** + * Resolve a job. If there are no more worker threads, the processor callback + * will be called. + */ +Processor.prototype._resolveJob = function() { + var job = this._job; + var threads = this._workers.length; + var data, meta; + if (threads === 1) { + data = new Uint8ClampedArray(this._dataLookup[0]['buffer']); + meta = this._dataLookup[0]['meta']; + } else { + var length = job.inputs[0].data.length; + data = new Uint8ClampedArray(length); + meta = new Array(length); + var segmentLength = 4 * Math.ceil(length / 4 / threads); + for (var i = 0; i < threads; ++i) { + var buffer = this._dataLookup[i]['buffer']; + var offset = i * segmentLength; + data.set(new Uint8ClampedArray(buffer), offset); + meta[i] = this._dataLookup[i]['meta']; + } + } + this._job = null; + this._dataLookup = {}; + job.callback(null, + newImageData(data, job.inputs[0].width, job.inputs[0].height), meta); + this._dispatch(); +}; + +module.exports = Processor; + +},{"./util":3}],3:[function(_dereq_,module,exports){ +var hasImageData = true; +try { + new ImageData(10, 10); +} catch (_) { + hasImageData = false; +} + +var context = document.createElement('canvas').getContext('2d'); + +function newImageData(data, width, height) { + if (hasImageData) { + return new ImageData(data, width, height); + } else { + var imageData = context.createImageData(width, height); + imageData.data.set(data); + return imageData; + } +} + +exports.newImageData = newImageData; + +},{}]},{},[1])(1) +}); +ol.ext.pixelworks = module.exports; +})(); + +goog.provide('ol.RasterOperationType'); +goog.provide('ol.source.Raster'); +goog.provide('ol.source.RasterEvent'); +goog.provide('ol.source.RasterEventType'); + +goog.require('goog.asserts'); +goog.require('goog.vec.Mat4'); +goog.require('ol.ImageCanvas'); +goog.require('ol.TileQueue'); +goog.require('ol.dom'); +goog.require('ol.events'); +goog.require('ol.events.Event'); +goog.require('ol.events.EventType'); +goog.require('ol.ext.pixelworks'); +goog.require('ol.extent'); +goog.require('ol.layer.Image'); +goog.require('ol.layer.Tile'); +goog.require('ol.object'); +goog.require('ol.renderer.canvas.ImageLayer'); +goog.require('ol.renderer.canvas.TileLayer'); +goog.require('ol.source.Image'); +goog.require('ol.source.State'); +goog.require('ol.source.Tile'); + + +/** + * Raster operation type. Supported values are `'pixel'` and `'image'`. + * @enum {string} + */ +ol.RasterOperationType = { + PIXEL: 'pixel', + IMAGE: 'image' +}; + + +/** + * @classdesc + * A source that transforms data from any number of input sources using an array + * of {@link ol.RasterOperation} functions to transform input pixel values into + * output pixel values. + * + * @constructor + * @extends {ol.source.Image} + * @fires ol.source.RasterEvent + * @param {olx.source.RasterOptions} options Options. + * @api + */ +ol.source.Raster = function(options) { + + /** + * @private + * @type {*} + */ + this.worker_ = null; + + /** + * @private + * @type {ol.RasterOperationType} + */ + this.operationType_ = options.operationType !== undefined ? + options.operationType : ol.RasterOperationType.PIXEL; + + /** + * @private + * @type {number} + */ + this.threads_ = options.threads !== undefined ? options.threads : 1; + + /** + * @private + * @type {Array.<ol.renderer.canvas.Layer>} + */ + this.renderers_ = ol.source.Raster.createRenderers_(options.sources); + + for (var r = 0, rr = this.renderers_.length; r < rr; ++r) { + ol.events.listen(this.renderers_[r], ol.events.EventType.CHANGE, + this.changed, this); + } + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.canvasContext_ = ol.dom.createCanvasContext2D(); + + /** + * @private + * @type {ol.TileQueue} + */ + this.tileQueue_ = new ol.TileQueue( + function() { + return 1; + }, + this.changed.bind(this)); + + var layerStatesArray = ol.source.Raster.getLayerStatesArray_(this.renderers_); + var layerStates = {}; + for (var i = 0, ii = layerStatesArray.length; i < ii; ++i) { + layerStates[goog.getUid(layerStatesArray[i].layer)] = layerStatesArray[i]; + } + + /** + * The most recently rendered state. + * @type {?ol.SourceRasterRenderedState} + * @private + */ + this.renderedState_ = null; + + /** + * The most recently rendered image canvas. + * @type {ol.ImageCanvas} + * @private + */ + this.renderedImageCanvas_ = null; + + /** + * @private + * @type {olx.FrameState} + */ + this.frameState_ = { + animate: false, + attributions: {}, + coordinateToPixelMatrix: goog.vec.Mat4.createNumber(), + extent: null, + focus: null, + index: 0, + layerStates: layerStates, + layerStatesArray: layerStatesArray, + logos: {}, + pixelRatio: 1, + pixelToCoordinateMatrix: goog.vec.Mat4.createNumber(), + postRenderFunctions: [], + size: [0, 0], + skippedFeatureUids: {}, + tileQueue: this.tileQueue_, + time: Date.now(), + usedTiles: {}, + viewState: /** @type {olx.ViewState} */ ({ + rotation: 0 + }), + viewHints: [], + wantedTiles: {} + }; + + ol.source.Image.call(this, {}); + + if (options.operation !== undefined) { + this.setOperation(options.operation, options.lib); + } + +}; +ol.inherits(ol.source.Raster, ol.source.Image); + + +/** + * Set the operation. + * @param {ol.RasterOperation} operation New operation. + * @param {Object=} opt_lib Functions that will be available to operations run + * in a worker. + * @api + */ +ol.source.Raster.prototype.setOperation = function(operation, opt_lib) { + this.worker_ = new ol.ext.pixelworks.Processor({ + operation: operation, + imageOps: this.operationType_ === ol.RasterOperationType.IMAGE, + queue: 1, + lib: opt_lib, + threads: this.threads_ + }); + this.changed(); +}; + + +/** + * Update the stored frame state. + * @param {ol.Extent} extent The view extent (in map units). + * @param {number} resolution The view resolution. + * @param {ol.proj.Projection} projection The view projection. + * @return {olx.FrameState} The updated frame state. + * @private + */ +ol.source.Raster.prototype.updateFrameState_ = function(extent, resolution, projection) { + + var frameState = /** @type {olx.FrameState} */ ( + ol.object.assign({}, this.frameState_)); + + frameState.viewState = /** @type {olx.ViewState} */ ( + ol.object.assign({}, frameState.viewState)); + + var center = ol.extent.getCenter(extent); + var width = Math.round(ol.extent.getWidth(extent) / resolution); + var height = Math.round(ol.extent.getHeight(extent) / resolution); + + frameState.extent = extent; + frameState.focus = ol.extent.getCenter(extent); + frameState.size[0] = width; + frameState.size[1] = height; + + var viewState = frameState.viewState; + viewState.center = center; + viewState.projection = projection; + viewState.resolution = resolution; + return frameState; +}; + + +/** + * Determine if the most recently rendered image canvas is dirty. + * @param {ol.Extent} extent The requested extent. + * @param {number} resolution The requested resolution. + * @return {boolean} The image is dirty. + * @private + */ +ol.source.Raster.prototype.isDirty_ = function(extent, resolution) { + var state = this.renderedState_; + return !state || + this.getRevision() !== state.revision || + resolution !== state.resolution || + !ol.extent.equals(extent, state.extent); +}; + + +/** + * @inheritDoc + */ +ol.source.Raster.prototype.getImage = function(extent, resolution, pixelRatio, projection) { + + if (!this.allSourcesReady_()) { + return null; + } + + var currentExtent = extent.slice(); + if (!this.isDirty_(currentExtent, resolution)) { + return this.renderedImageCanvas_; + } + + var context = this.canvasContext_; + var canvas = context.canvas; + + var width = Math.round(ol.extent.getWidth(currentExtent) / resolution); + var height = Math.round(ol.extent.getHeight(currentExtent) / resolution); + + if (width !== canvas.width || + height !== canvas.height) { + canvas.width = width; + canvas.height = height; + } + + var frameState = this.updateFrameState_(currentExtent, resolution, projection); + + var imageCanvas = new ol.ImageCanvas( + currentExtent, resolution, 1, this.getAttributions(), canvas, + this.composeFrame_.bind(this, frameState)); + + this.renderedImageCanvas_ = imageCanvas; + + this.renderedState_ = { + extent: currentExtent, + resolution: resolution, + revision: this.getRevision() + }; + + return imageCanvas; +}; + + +/** + * Determine if all sources are ready. + * @return {boolean} All sources are ready. + * @private + */ +ol.source.Raster.prototype.allSourcesReady_ = function() { + var ready = true; + var source; + for (var i = 0, ii = this.renderers_.length; i < ii; ++i) { + source = this.renderers_[i].getLayer().getSource(); + if (source.getState() !== ol.source.State.READY) { + ready = false; + break; + } + } + return ready; +}; + + +/** + * Compose the frame. This renders data from all sources, runs pixel-wise + * operations, and renders the result to the stored canvas context. + * @param {olx.FrameState} frameState The frame state. + * @param {function(Error)} callback Called when composition is complete. + * @private + */ +ol.source.Raster.prototype.composeFrame_ = function(frameState, callback) { + var len = this.renderers_.length; + var imageDatas = new Array(len); + for (var i = 0; i < len; ++i) { + var imageData = ol.source.Raster.getImageData_( + this.renderers_[i], frameState, frameState.layerStatesArray[i]); + if (imageData) { + imageDatas[i] = imageData; + } else { + // image not yet ready + return; + } + } + + var data = {}; + this.dispatchEvent(new ol.source.RasterEvent( + ol.source.RasterEventType.BEFOREOPERATIONS, frameState, data)); + + this.worker_.process(imageDatas, data, + this.onWorkerComplete_.bind(this, frameState, callback)); + + frameState.tileQueue.loadMoreTiles(16, 16); +}; + + +/** + * Called when pixel processing is complete. + * @param {olx.FrameState} frameState The frame state. + * @param {function(Error)} callback Called when rendering is complete. + * @param {Error} err Any error during processing. + * @param {ImageData} output The output image data. + * @param {Object} data The user data. + * @private + */ +ol.source.Raster.prototype.onWorkerComplete_ = function(frameState, callback, err, output, data) { + if (err) { + callback(err); + return; + } + if (!output) { + // job aborted + return; + } + + this.dispatchEvent(new ol.source.RasterEvent( + ol.source.RasterEventType.AFTEROPERATIONS, frameState, data)); + + var resolution = frameState.viewState.resolution / frameState.pixelRatio; + if (!this.isDirty_(frameState.extent, resolution)) { + this.canvasContext_.putImageData(output, 0, 0); + } + + callback(null); +}; + + +/** + * Get image data from a renderer. + * @param {ol.renderer.canvas.Layer} renderer Layer renderer. + * @param {olx.FrameState} frameState The frame state. + * @param {ol.LayerState} layerState The layer state. + * @return {ImageData} The image data. + * @private + */ +ol.source.Raster.getImageData_ = function(renderer, frameState, layerState) { + if (!renderer.prepareFrame(frameState, layerState)) { + return null; + } + var width = frameState.size[0]; + var height = frameState.size[1]; + if (!ol.source.Raster.context_) { + ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height); + } else { + var canvas = ol.source.Raster.context_.canvas; + if (canvas.width !== width || canvas.height !== height) { + ol.source.Raster.context_ = ol.dom.createCanvasContext2D(width, height); + } else { + ol.source.Raster.context_.clearRect(0, 0, width, height); + } + } + renderer.composeFrame(frameState, layerState, ol.source.Raster.context_); + return ol.source.Raster.context_.getImageData(0, 0, width, height); +}; + + +/** + * A reusable canvas context. + * @type {CanvasRenderingContext2D} + * @private + */ +ol.source.Raster.context_ = null; + + +/** + * Get a list of layer states from a list of renderers. + * @param {Array.<ol.renderer.canvas.Layer>} renderers Layer renderers. + * @return {Array.<ol.LayerState>} The layer states. + * @private + */ +ol.source.Raster.getLayerStatesArray_ = function(renderers) { + return renderers.map(function(renderer) { + return renderer.getLayer().getLayerState(); + }); +}; + + +/** + * Create renderers for all sources. + * @param {Array.<ol.source.Source>} sources The sources. + * @return {Array.<ol.renderer.canvas.Layer>} Array of layer renderers. + * @private + */ +ol.source.Raster.createRenderers_ = function(sources) { + var len = sources.length; + var renderers = new Array(len); + for (var i = 0; i < len; ++i) { + renderers[i] = ol.source.Raster.createRenderer_(sources[i]); + } + return renderers; +}; + + +/** + * Create a renderer for the provided source. + * @param {ol.source.Source} source The source. + * @return {ol.renderer.canvas.Layer} The renderer. + * @private + */ +ol.source.Raster.createRenderer_ = function(source) { + var renderer = null; + if (source instanceof ol.source.Tile) { + renderer = ol.source.Raster.createTileRenderer_(source); + } else if (source instanceof ol.source.Image) { + renderer = ol.source.Raster.createImageRenderer_(source); + } else { + goog.asserts.fail('Unsupported source type: ' + source); + } + return renderer; +}; + + +/** + * Create an image renderer for the provided source. + * @param {ol.source.Image} source The source. + * @return {ol.renderer.canvas.Layer} The renderer. + * @private + */ +ol.source.Raster.createImageRenderer_ = function(source) { + var layer = new ol.layer.Image({source: source}); + return new ol.renderer.canvas.ImageLayer(layer); +}; + + +/** + * Create a tile renderer for the provided source. + * @param {ol.source.Tile} source The source. + * @return {ol.renderer.canvas.Layer} The renderer. + * @private + */ +ol.source.Raster.createTileRenderer_ = function(source) { + var layer = new ol.layer.Tile({source: source}); + return new ol.renderer.canvas.TileLayer(layer); +}; + + +/** + * @classdesc + * Events emitted by {@link ol.source.Raster} instances are instances of this + * type. + * + * @constructor + * @extends {ol.events.Event} + * @implements {oli.source.RasterEvent} + * @param {string} type Type. + * @param {olx.FrameState} frameState The frame state. + * @param {Object} data An object made available to operations. + */ +ol.source.RasterEvent = function(type, frameState, data) { + ol.events.Event.call(this, type); + + /** + * The raster extent. + * @type {ol.Extent} + * @api + */ + this.extent = frameState.extent; + + /** + * The pixel resolution (map units per pixel). + * @type {number} + * @api + */ + this.resolution = frameState.viewState.resolution / frameState.pixelRatio; + + /** + * An object made available to all operations. This can be used by operations + * as a storage object (e.g. for calculating statistics). + * @type {Object} + * @api + */ + this.data = data; + +}; +ol.inherits(ol.source.RasterEvent, ol.events.Event); + + +/** + * @enum {string} + */ +ol.source.RasterEventType = { + /** + * Triggered before operations are run. + * @event ol.source.RasterEvent#beforeoperations + * @api + */ + BEFOREOPERATIONS: 'beforeoperations', + + /** + * Triggered after operations are run. + * @event ol.source.RasterEvent#afteroperations + * @api + */ + AFTEROPERATIONS: 'afteroperations' +}; + +goog.provide('ol.source.Stamen'); + +goog.require('goog.asserts'); +goog.require('ol.Attribution'); +goog.require('ol.source.OSM'); +goog.require('ol.source.XYZ'); + + +/** + * @type {Object.<string, {extension: string, opaque: boolean}>} + */ +ol.source.StamenLayerConfig = { + 'terrain': { + extension: 'jpg', + opaque: true + }, + 'terrain-background': { + extension: 'jpg', + opaque: true + }, + 'terrain-labels': { + extension: 'png', + opaque: false + }, + 'terrain-lines': { + extension: 'png', + opaque: false + }, + 'toner-background': { + extension: 'png', + opaque: true + }, + 'toner': { + extension: 'png', + opaque: true + }, + 'toner-hybrid': { + extension: 'png', + opaque: false + }, + 'toner-labels': { + extension: 'png', + opaque: false + }, + 'toner-lines': { + extension: 'png', + opaque: false + }, + 'toner-lite': { + extension: 'png', + opaque: true + }, + 'watercolor': { + extension: 'jpg', + opaque: true + } +}; + + +/** + * @type {Object.<string, {minZoom: number, maxZoom: number}>} + */ +ol.source.StamenProviderConfig = { + 'terrain': { + minZoom: 4, + maxZoom: 18 + }, + 'toner': { + minZoom: 0, + maxZoom: 20 + }, + 'watercolor': { + minZoom: 1, + maxZoom: 16 + } +}; + + +/** + * @classdesc + * Layer source for the Stamen tile server. + * + * @constructor + * @extends {ol.source.XYZ} + * @param {olx.source.StamenOptions} options Stamen options. + * @api stable + */ +ol.source.Stamen = function(options) { + + var i = options.layer.indexOf('-'); + var provider = i == -1 ? options.layer : options.layer.slice(0, i); + goog.asserts.assert(provider in ol.source.StamenProviderConfig, + 'known provider configured'); + var providerConfig = ol.source.StamenProviderConfig[provider]; + + goog.asserts.assert(options.layer in ol.source.StamenLayerConfig, + 'known layer configured'); + var layerConfig = ol.source.StamenLayerConfig[options.layer]; + + var url = options.url !== undefined ? options.url : + 'https://stamen-tiles-{a-d}.a.ssl.fastly.net/' + options.layer + + '/{z}/{x}/{y}.' + layerConfig.extension; + + ol.source.XYZ.call(this, { + attributions: ol.source.Stamen.ATTRIBUTIONS, + cacheSize: options.cacheSize, + crossOrigin: 'anonymous', + maxZoom: options.maxZoom != undefined ? options.maxZoom : providerConfig.maxZoom, + minZoom: options.minZoom != undefined ? options.minZoom : providerConfig.minZoom, + opaque: layerConfig.opaque, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileLoadFunction: options.tileLoadFunction, + url: url + }); + +}; +ol.inherits(ol.source.Stamen, ol.source.XYZ); + + +/** + * @const + * @type {Array.<ol.Attribution>} + */ +ol.source.Stamen.ATTRIBUTIONS = [ + new ol.Attribution({ + html: 'Map tiles by <a href="http://stamen.com/">Stamen Design</a>, ' + + 'under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY' + + ' 3.0</a>.' + }), + ol.source.OSM.ATTRIBUTION +]; + +goog.provide('ol.source.TileArcGISRest'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.math'); +goog.require('ol.proj'); +goog.require('ol.size'); +goog.require('ol.source.TileImage'); +goog.require('ol.tilecoord'); +goog.require('ol.uri'); + + +/** + * @classdesc + * Layer source for tile data from ArcGIS Rest services. Map and Image + * Services are supported. + * + * For cached ArcGIS services, better performance is available using the + * {@link ol.source.XYZ} data source. + * + * @constructor + * @extends {ol.source.TileImage} + * @param {olx.source.TileArcGISRestOptions=} opt_options Tile ArcGIS Rest + * options. + * @api + */ +ol.source.TileArcGISRest = function(opt_options) { + + var options = opt_options || {}; + + ol.source.TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + logo: options.logo, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileGrid: options.tileGrid, + tileLoadFunction: options.tileLoadFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); + + /** + * @private + * @type {!Object} + */ + this.params_ = options.params || {}; + + /** + * @private + * @type {ol.Extent} + */ + this.tmpExtent_ = ol.extent.createEmpty(); + +}; +ol.inherits(ol.source.TileArcGISRest, ol.source.TileImage); + + +/** + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api + */ +ol.source.TileArcGISRest.prototype.getParams = function() { + return this.params_; +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.Size} tileSize Tile size. + * @param {ol.Extent} tileExtent Tile extent. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @param {Object} params Params. + * @return {string|undefined} Request URL. + * @private + */ +ol.source.TileArcGISRest.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent, + pixelRatio, projection, params) { + + var urls = this.urls; + if (!urls) { + return undefined; + } + + // ArcGIS Server only wants the numeric portion of the projection ID. + var srid = projection.getCode().split(':').pop(); + + params['SIZE'] = tileSize[0] + ',' + tileSize[1]; + params['BBOX'] = tileExtent.join(','); + params['BBOXSR'] = srid; + params['IMAGESR'] = srid; + params['DPI'] = Math.round( + params['DPI'] ? params['DPI'] * pixelRatio : 90 * pixelRatio + ); + + var url; + if (urls.length == 1) { + url = urls[0]; + } else { + var index = ol.math.modulo(ol.tilecoord.hash(tileCoord), urls.length); + url = urls[index]; + } + + var modifiedUrl = url + .replace(/MapServer\/?$/, 'MapServer/export') + .replace(/ImageServer\/?$/, 'ImageServer/exportImage'); + if (modifiedUrl == url) { + goog.asserts.fail('Unknown Rest Service', url); + } + return ol.uri.appendParams(modifiedUrl, params); +}; + + +/** + * @inheritDoc + */ +ol.source.TileArcGISRest.prototype.getTilePixelRatio = function(pixelRatio) { + return pixelRatio; +}; + + +/** + * @inheritDoc + */ +ol.source.TileArcGISRest.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) { + + var tileGrid = this.getTileGrid(); + if (!tileGrid) { + tileGrid = this.getTileGridForProjection(projection); + } + + if (tileGrid.getResolutions().length <= tileCoord[0]) { + return undefined; + } + + var tileExtent = tileGrid.getTileCoordExtent( + tileCoord, this.tmpExtent_); + var tileSize = ol.size.toSize( + tileGrid.getTileSize(tileCoord[0]), this.tmpSize); + + if (pixelRatio != 1) { + tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize); + } + + // Apply default params and override with user specified values. + var baseParams = { + 'F': 'image', + 'FORMAT': 'PNG32', + 'TRANSPARENT': true + }; + ol.object.assign(baseParams, this.params_); + + return this.getRequestUrl_(tileCoord, tileSize, tileExtent, + pixelRatio, projection, baseParams); +}; + + +/** + * Update the user-provided params. + * @param {Object} params Params. + * @api stable + */ +ol.source.TileArcGISRest.prototype.updateParams = function(params) { + ol.object.assign(this.params_, params); + this.changed(); +}; + +goog.provide('ol.source.TileDebug'); + +goog.require('ol.Tile'); +goog.require('ol.TileState'); +goog.require('ol.dom'); +goog.require('ol.size'); +goog.require('ol.source.Tile'); + + +/** + * @constructor + * @extends {ol.Tile} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.Size} tileSize Tile size. + * @param {string} text Text. + * @private + */ +ol.DebugTile_ = function(tileCoord, tileSize, text) { + + ol.Tile.call(this, tileCoord, ol.TileState.LOADED); + + /** + * @private + * @type {ol.Size} + */ + this.tileSize_ = tileSize; + + /** + * @private + * @type {string} + */ + this.text_ = text; + + /** + * @private + * @type {Object.<number, HTMLCanvasElement>} + */ + this.canvasByContext_ = {}; + +}; +ol.inherits(ol.DebugTile_, ol.Tile); + + +/** + * Get the image element for this tile. + * @param {Object=} opt_context Optional context. Only used by the DOM + * renderer. + * @return {HTMLCanvasElement} Image. + */ +ol.DebugTile_.prototype.getImage = function(opt_context) { + var key = opt_context !== undefined ? goog.getUid(opt_context) : -1; + if (key in this.canvasByContext_) { + return this.canvasByContext_[key]; + } else { + + var tileSize = this.tileSize_; + var context = ol.dom.createCanvasContext2D(tileSize[0], tileSize[1]); + + context.strokeStyle = 'black'; + context.strokeRect(0.5, 0.5, tileSize[0] + 0.5, tileSize[1] + 0.5); + + context.fillStyle = 'black'; + context.textAlign = 'center'; + context.textBaseline = 'middle'; + context.font = '24px sans-serif'; + context.fillText(this.text_, tileSize[0] / 2, tileSize[1] / 2); + + this.canvasByContext_[key] = context.canvas; + return context.canvas; + + } +}; + + +/** + * @classdesc + * A pseudo tile source, which does not fetch tiles from a server, but renders + * a grid outline for the tile grid/projection along with the coordinates for + * each tile. See examples/canvas-tiles for an example. + * + * Uses Canvas context2d, so requires Canvas support. + * + * @constructor + * @extends {ol.source.Tile} + * @param {olx.source.TileDebugOptions} options Debug tile options. + * @api + */ +ol.source.TileDebug = function(options) { + + ol.source.Tile.call(this, { + opaque: false, + projection: options.projection, + tileGrid: options.tileGrid, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); + +}; +ol.inherits(ol.source.TileDebug, ol.source.Tile); + + +/** + * @inheritDoc + */ +ol.source.TileDebug.prototype.getTile = function(z, x, y) { + var tileCoordKey = this.getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + return /** @type {!ol.DebugTile_} */ (this.tileCache.get(tileCoordKey)); + } else { + var tileSize = ol.size.toSize(this.tileGrid.getTileSize(z)); + var tileCoord = [z, x, y]; + var textTileCoord = this.getTileCoordForTileUrlFunction(tileCoord); + var text = !textTileCoord ? '' : + this.getTileCoordForTileUrlFunction(textTileCoord).toString(); + var tile = new ol.DebugTile_(tileCoord, tileSize, text); + this.tileCache.set(tileCoordKey, tile); + return tile; + } +}; + +// FIXME check order of async callbacks + +/** + * @see http://mapbox.com/developers/api/ + */ + +goog.provide('ol.source.TileJSON'); +goog.provide('ol.tilejson'); + +goog.require('goog.asserts'); +goog.require('ol.Attribution'); +goog.require('ol.TileRange'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.extent'); +goog.require('ol.net'); +goog.require('ol.proj'); +goog.require('ol.source.State'); +goog.require('ol.source.TileImage'); + + +/** + * @classdesc + * Layer source for tile data in TileJSON format. + * + * @constructor + * @extends {ol.source.TileImage} + * @param {olx.source.TileJSONOptions} options TileJSON options. + * @api stable + */ +ol.source.TileJSON = function(options) { + + /** + * @type {TileJSON} + * @private + */ + this.tileJSON_ = null; + + ol.source.TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + projection: ol.proj.get('EPSG:3857'), + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + state: ol.source.State.LOADING, + tileLoadFunction: options.tileLoadFunction, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); + + if (options.jsonp) { + ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this), + this.handleTileJSONError.bind(this)); + } else { + var client = new XMLHttpRequest(); + client.addEventListener('load', this.onXHRLoad_.bind(this)); + client.addEventListener('error', this.onXHRError_.bind(this)); + client.open('GET', options.url); + client.send(); + } + +}; +ol.inherits(ol.source.TileJSON, ol.source.TileImage); + + +/** + * @private + * @param {Event} event The load event. + */ +ol.source.TileJSON.prototype.onXHRLoad_ = function(event) { + var client = /** @type {XMLHttpRequest} */ (event.target); + if (client.status >= 200 && client.status < 300) { + var response; + try { + response = /** @type {TileJSON} */(JSON.parse(client.responseText)); + } catch (err) { + this.handleTileJSONError(); + return; + } + this.handleTileJSONResponse(response); + } else { + this.handleTileJSONError(); + } +}; + + +/** + * @private + * @param {Event} event The error event. + */ +ol.source.TileJSON.prototype.onXHRError_ = function(event) { + this.handleTileJSONError(); +}; + + +/** + * @return {TileJSON} The tilejson object. + * @api + */ +ol.source.TileJSON.prototype.getTileJSON = function() { + return this.tileJSON_; +}; + + +/** + * @protected + * @param {TileJSON} tileJSON Tile JSON. + */ +ol.source.TileJSON.prototype.handleTileJSONResponse = function(tileJSON) { + + var epsg4326Projection = ol.proj.get('EPSG:4326'); + + var sourceProjection = this.getProjection(); + var extent; + if (tileJSON.bounds !== undefined) { + var transform = ol.proj.getTransformFromProjections( + epsg4326Projection, sourceProjection); + extent = ol.extent.applyTransform(tileJSON.bounds, transform); + } + + if (tileJSON.scheme !== undefined) { + goog.asserts.assert(tileJSON.scheme == 'xyz', 'tileJSON-scheme is "xyz"'); + } + var minZoom = tileJSON.minzoom || 0; + var maxZoom = tileJSON.maxzoom || 22; + var tileGrid = ol.tilegrid.createXYZ({ + extent: ol.tilegrid.extentFromProjection(sourceProjection), + maxZoom: maxZoom, + minZoom: minZoom + }); + this.tileGrid = tileGrid; + + this.tileUrlFunction = + ol.TileUrlFunction.createFromTemplates(tileJSON.tiles, tileGrid); + + if (tileJSON.attribution !== undefined && !this.getAttributions()) { + var attributionExtent = extent !== undefined ? + extent : epsg4326Projection.getExtent(); + /** @type {Object.<string, Array.<ol.TileRange>>} */ + var tileRanges = {}; + var z, zKey; + for (z = minZoom; z <= maxZoom; ++z) { + zKey = z.toString(); + tileRanges[zKey] = + [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)]; + } + this.setAttributions([ + new ol.Attribution({ + html: tileJSON.attribution, + tileRanges: tileRanges + }) + ]); + } + this.tileJSON_ = tileJSON; + this.setState(ol.source.State.READY); + +}; + + +/** + * @protected + */ +ol.source.TileJSON.prototype.handleTileJSONError = function() { + this.setState(ol.source.State.ERROR); +}; + +goog.provide('ol.source.TileUTFGrid'); + +goog.require('goog.asserts'); +goog.require('goog.async.nextTick'); +goog.require('ol.Attribution'); +goog.require('ol.Tile'); +goog.require('ol.TileState'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.events'); +goog.require('ol.events.EventType'); +goog.require('ol.extent'); +goog.require('ol.net'); +goog.require('ol.proj'); +goog.require('ol.source.State'); +goog.require('ol.source.Tile'); + + +/** + * @classdesc + * Layer source for UTFGrid interaction data loaded from TileJSON format. + * + * @constructor + * @extends {ol.source.Tile} + * @param {olx.source.TileUTFGridOptions} options Source options. + * @api + */ +ol.source.TileUTFGrid = function(options) { + ol.source.Tile.call(this, { + projection: ol.proj.get('EPSG:3857'), + state: ol.source.State.LOADING + }); + + /** + * @private + * @type {boolean} + */ + this.preemptive_ = options.preemptive !== undefined ? + options.preemptive : true; + + /** + * @private + * @type {!ol.TileUrlFunctionType} + */ + this.tileUrlFunction_ = ol.TileUrlFunction.nullTileUrlFunction; + + /** + * @private + * @type {string|undefined} + */ + this.template_ = undefined; + + /** + * @private + * @type {boolean} + */ + this.jsonp_ = options.jsonp || false; + + if (options.url) { + if (this.jsonp_) { + ol.net.jsonp(options.url, this.handleTileJSONResponse.bind(this), + this.handleTileJSONError.bind(this)); + } else { + var client = new XMLHttpRequest(); + client.addEventListener('load', this.onXHRLoad_.bind(this)); + client.addEventListener('error', this.onXHRError_.bind(this)); + client.open('GET', options.url); + client.send(); + } + } else if (options.tileJSON) { + this.handleTileJSONResponse(options.tileJSON); + } else { + goog.asserts.fail('Either url or tileJSON options must be provided'); + } +}; +ol.inherits(ol.source.TileUTFGrid, ol.source.Tile); + + +/** + * @private + * @param {Event} event The load event. + */ +ol.source.TileUTFGrid.prototype.onXHRLoad_ = function(event) { + var client = /** @type {XMLHttpRequest} */ (event.target); + if (client.status >= 200 && client.status < 300) { + var response; + try { + response = /** @type {TileJSON} */(JSON.parse(client.responseText)); + } catch (err) { + this.handleTileJSONError(); + return; + } + this.handleTileJSONResponse(response); + } else { + this.handleTileJSONError(); + } +}; + + +/** + * @private + * @param {Event} event The error event. + */ +ol.source.TileUTFGrid.prototype.onXHRError_ = function(event) { + this.handleTileJSONError(); +}; + + +/** + * Return the template from TileJSON. + * @return {string|undefined} The template from TileJSON. + * @api + */ +ol.source.TileUTFGrid.prototype.getTemplate = function() { + return this.template_; +}; + + +/** + * Calls the callback (synchronously by default) with the available data + * for given coordinate and resolution (or `null` if not yet loaded or + * in case of an error). + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {function(this: T, *)} callback Callback. + * @param {T=} opt_this The object to use as `this` in the callback. + * @param {boolean=} opt_request If `true` the callback is always async. + * The tile data is requested if not yet loaded. + * @template T + * @api + */ +ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution = function( + coordinate, resolution, callback, opt_this, opt_request) { + if (this.tileGrid) { + var tileCoord = this.tileGrid.getTileCoordForCoordAndResolution( + coordinate, resolution); + var tile = /** @type {!ol.source.TileUTFGridTile_} */(this.getTile( + tileCoord[0], tileCoord[1], tileCoord[2], 1, this.getProjection())); + tile.forDataAtCoordinate(coordinate, callback, opt_this, opt_request); + } else { + if (opt_request === true) { + goog.async.nextTick(function() { + callback.call(opt_this, null); + }); + } else { + callback.call(opt_this, null); + } + } +}; + + +/** + * @protected + */ +ol.source.TileUTFGrid.prototype.handleTileJSONError = function() { + this.setState(ol.source.State.ERROR); +}; + + +/** + * TODO: very similar to ol.source.TileJSON#handleTileJSONResponse + * @protected + * @param {TileJSON} tileJSON Tile JSON. + */ +ol.source.TileUTFGrid.prototype.handleTileJSONResponse = function(tileJSON) { + + var epsg4326Projection = ol.proj.get('EPSG:4326'); + + var sourceProjection = this.getProjection(); + var extent; + if (tileJSON.bounds !== undefined) { + var transform = ol.proj.getTransformFromProjections( + epsg4326Projection, sourceProjection); + extent = ol.extent.applyTransform(tileJSON.bounds, transform); + } + + if (tileJSON.scheme !== undefined) { + goog.asserts.assert(tileJSON.scheme == 'xyz', 'tileJSON-scheme is "xyz"'); + } + var minZoom = tileJSON.minzoom || 0; + var maxZoom = tileJSON.maxzoom || 22; + var tileGrid = ol.tilegrid.createXYZ({ + extent: ol.tilegrid.extentFromProjection(sourceProjection), + maxZoom: maxZoom, + minZoom: minZoom + }); + this.tileGrid = tileGrid; + + this.template_ = tileJSON.template; + + var grids = tileJSON.grids; + if (!grids) { + this.setState(ol.source.State.ERROR); + return; + } + + this.tileUrlFunction_ = + ol.TileUrlFunction.createFromTemplates(grids, tileGrid); + + if (tileJSON.attribution !== undefined) { + var attributionExtent = extent !== undefined ? + extent : epsg4326Projection.getExtent(); + /** @type {Object.<string, Array.<ol.TileRange>>} */ + var tileRanges = {}; + var z, zKey; + for (z = minZoom; z <= maxZoom; ++z) { + zKey = z.toString(); + tileRanges[zKey] = + [tileGrid.getTileRangeForExtentAndZ(attributionExtent, z)]; + } + this.setAttributions([ + new ol.Attribution({ + html: tileJSON.attribution, + tileRanges: tileRanges + }) + ]); + } + + this.setState(ol.source.State.READY); + +}; + + +/** + * @inheritDoc + */ +ol.source.TileUTFGrid.prototype.getTile = function(z, x, y, pixelRatio, projection) { + var tileCoordKey = this.getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey)); + } else { + goog.asserts.assert(projection, 'argument projection is truthy'); + var tileCoord = [z, x, y]; + var urlTileCoord = + this.getTileCoordForTileUrlFunction(tileCoord, projection); + var tileUrl = this.tileUrlFunction_(urlTileCoord, pixelRatio, projection); + var tile = new ol.source.TileUTFGridTile_( + tileCoord, + tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY, + tileUrl !== undefined ? tileUrl : '', + this.tileGrid.getTileCoordExtent(tileCoord), + this.preemptive_, + this.jsonp_); + this.tileCache.set(tileCoordKey, tile); + return tile; + } +}; + + +/** + * @inheritDoc + */ +ol.source.TileUTFGrid.prototype.useTile = function(z, x, y) { + var tileCoordKey = this.getKeyZXY(z, x, y); + if (this.tileCache.containsKey(tileCoordKey)) { + this.tileCache.get(tileCoordKey); + } +}; + + +/** + * @constructor + * @extends {ol.Tile} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.TileState} state State. + * @param {string} src Image source URI. + * @param {ol.Extent} extent Extent of the tile. + * @param {boolean} preemptive Load the tile when visible (before it's needed). + * @param {boolean} jsonp Load the tile as a script. + * @private + */ +ol.source.TileUTFGridTile_ = function(tileCoord, state, src, extent, preemptive, jsonp) { + + ol.Tile.call(this, tileCoord, state); + + /** + * @private + * @type {string} + */ + this.src_ = src; + + /** + * @private + * @type {ol.Extent} + */ + this.extent_ = extent; + + /** + * @private + * @type {boolean} + */ + this.preemptive_ = preemptive; + + /** + * @private + * @type {Array.<string>} + */ + this.grid_ = null; + + /** + * @private + * @type {Array.<string>} + */ + this.keys_ = null; + + /** + * @private + * @type {Object.<string, Object>|undefined} + */ + this.data_ = null; + + + /** + * @private + * @type {boolean} + */ + this.jsonp_ = jsonp; + +}; +ol.inherits(ol.source.TileUTFGridTile_, ol.Tile); + + +/** + * Get the image element for this tile. + * @param {Object=} opt_context Optional context. Only used for the DOM + * renderer. + * @return {Image} Image. + */ +ol.source.TileUTFGridTile_.prototype.getImage = function(opt_context) { + return null; +}; + + +/** + * Synchronously returns data at given coordinate (if available). + * @param {ol.Coordinate} coordinate Coordinate. + * @return {*} The data. + */ +ol.source.TileUTFGridTile_.prototype.getData = function(coordinate) { + if (!this.grid_ || !this.keys_) { + return null; + } + var xRelative = (coordinate[0] - this.extent_[0]) / + (this.extent_[2] - this.extent_[0]); + var yRelative = (coordinate[1] - this.extent_[1]) / + (this.extent_[3] - this.extent_[1]); + + var row = this.grid_[Math.floor((1 - yRelative) * this.grid_.length)]; + + if (typeof row !== 'string') { + return null; + } + + var code = row.charCodeAt(Math.floor(xRelative * row.length)); + if (code >= 93) { + code--; + } + if (code >= 35) { + code--; + } + code -= 32; + + var data = null; + if (code in this.keys_) { + var id = this.keys_[code]; + if (this.data_ && id in this.data_) { + data = this.data_[id]; + } else { + data = id; + } + } + return data; +}; + + +/** + * Calls the callback (synchronously by default) with the available data + * for given coordinate (or `null` if not yet loaded). + * @param {ol.Coordinate} coordinate Coordinate. + * @param {function(this: T, *)} callback Callback. + * @param {T=} opt_this The object to use as `this` in the callback. + * @param {boolean=} opt_request If `true` the callback is always async. + * The tile data is requested if not yet loaded. + * @template T + */ +ol.source.TileUTFGridTile_.prototype.forDataAtCoordinate = function(coordinate, callback, opt_this, opt_request) { + if (this.state == ol.TileState.IDLE && opt_request === true) { + ol.events.listenOnce(this, ol.events.EventType.CHANGE, function(e) { + callback.call(opt_this, this.getData(coordinate)); + }, this); + this.loadInternal_(); + } else { + if (opt_request === true) { + goog.async.nextTick(function() { + callback.call(opt_this, this.getData(coordinate)); + }, this); + } else { + callback.call(opt_this, this.getData(coordinate)); + } + } +}; + + +/** + * @inheritDoc + */ +ol.source.TileUTFGridTile_.prototype.getKey = function() { + return this.src_; +}; + + +/** + * @private + */ +ol.source.TileUTFGridTile_.prototype.handleError_ = function() { + this.state = ol.TileState.ERROR; + this.changed(); +}; + + +/** + * @param {!UTFGridJSON} json UTFGrid data. + * @private + */ +ol.source.TileUTFGridTile_.prototype.handleLoad_ = function(json) { + this.grid_ = json.grid; + this.keys_ = json.keys; + this.data_ = json.data; + + this.state = ol.TileState.EMPTY; + this.changed(); +}; + + +/** + * @private + */ +ol.source.TileUTFGridTile_.prototype.loadInternal_ = function() { + if (this.state == ol.TileState.IDLE) { + this.state = ol.TileState.LOADING; + if (this.jsonp_) { + ol.net.jsonp(this.src_, this.handleLoad_.bind(this), + this.handleError_.bind(this)); + } else { + var client = new XMLHttpRequest(); + client.addEventListener('load', this.onXHRLoad_.bind(this)); + client.addEventListener('error', this.onXHRError_.bind(this)); + client.open('GET', this.src_); + client.send(); + } + } +}; + + +/** + * @private + * @param {Event} event The load event. + */ +ol.source.TileUTFGridTile_.prototype.onXHRLoad_ = function(event) { + var client = /** @type {XMLHttpRequest} */ (event.target); + if (client.status >= 200 && client.status < 300) { + var response; + try { + response = /** @type {!UTFGridJSON} */(JSON.parse(client.responseText)); + } catch (err) { + this.handleError_(); + return; + } + this.handleLoad_(response); + } else { + this.handleError_(); + } +}; + + +/** + * @private + * @param {Event} event The error event. + */ +ol.source.TileUTFGridTile_.prototype.onXHRError_ = function(event) { + this.handleError_(); +}; + + +/** + * Load not yet loaded URI. + */ +ol.source.TileUTFGridTile_.prototype.load = function() { + if (this.preemptive_) { + this.loadInternal_(); + } +}; + +// FIXME add minZoom support +// FIXME add date line wrap (tile coord transform) +// FIXME cannot be shared between maps with different projections + +goog.provide('ol.source.TileWMS'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.math'); +goog.require('ol.proj'); +goog.require('ol.size'); +goog.require('ol.source.TileImage'); +goog.require('ol.source.wms'); +goog.require('ol.source.wms.ServerType'); +goog.require('ol.tilecoord'); +goog.require('ol.string'); +goog.require('ol.uri'); + +/** + * @classdesc + * Layer source for tile data from WMS servers. + * + * @constructor + * @extends {ol.source.TileImage} + * @param {olx.source.TileWMSOptions=} opt_options Tile WMS options. + * @api stable + */ +ol.source.TileWMS = function(opt_options) { + + var options = opt_options || {}; + + var params = options.params || {}; + + var transparent = 'TRANSPARENT' in params ? params['TRANSPARENT'] : true; + + ol.source.TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + logo: options.logo, + opaque: !transparent, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileGrid: options.tileGrid, + tileLoadFunction: options.tileLoadFunction, + url: options.url, + urls: options.urls, + wrapX: options.wrapX !== undefined ? options.wrapX : true + }); + + /** + * @private + * @type {number} + */ + this.gutter_ = options.gutter !== undefined ? options.gutter : 0; + + /** + * @private + * @type {!Object} + */ + this.params_ = params; + + /** + * @private + * @type {boolean} + */ + this.v13_ = true; + + /** + * @private + * @type {ol.source.wms.ServerType|undefined} + */ + this.serverType_ = + /** @type {ol.source.wms.ServerType|undefined} */ (options.serverType); + + /** + * @private + * @type {boolean} + */ + this.hidpi_ = options.hidpi !== undefined ? options.hidpi : true; + + /** + * @private + * @type {string} + */ + this.coordKeyPrefix_ = ''; + this.resetCoordKeyPrefix_(); + + /** + * @private + * @type {ol.Extent} + */ + this.tmpExtent_ = ol.extent.createEmpty(); + + this.updateV13_(); + this.setKey(this.getKeyForParams_()); + +}; +ol.inherits(ol.source.TileWMS, ol.source.TileImage); + + +/** + * Return the GetFeatureInfo URL for the passed coordinate, resolution, and + * projection. Return `undefined` if the GetFeatureInfo URL cannot be + * constructed. + * @param {ol.Coordinate} coordinate Coordinate. + * @param {number} resolution Resolution. + * @param {ol.ProjectionLike} projection Projection. + * @param {!Object} params GetFeatureInfo params. `INFO_FORMAT` at least should + * be provided. If `QUERY_LAYERS` is not provided then the layers specified + * in the `LAYERS` parameter will be used. `VERSION` should not be + * specified here. + * @return {string|undefined} GetFeatureInfo URL. + * @api stable + */ +ol.source.TileWMS.prototype.getGetFeatureInfoUrl = function(coordinate, resolution, projection, params) { + + goog.asserts.assert(!('VERSION' in params), + 'key VERSION is not allowed in params'); + + var projectionObj = ol.proj.get(projection); + + var tileGrid = this.getTileGrid(); + if (!tileGrid) { + tileGrid = this.getTileGridForProjection(projectionObj); + } + + var tileCoord = tileGrid.getTileCoordForCoordAndResolution( + coordinate, resolution); + + if (tileGrid.getResolutions().length <= tileCoord[0]) { + return undefined; + } + + var tileResolution = tileGrid.getResolution(tileCoord[0]); + var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); + var tileSize = ol.size.toSize( + tileGrid.getTileSize(tileCoord[0]), this.tmpSize); + + var gutter = this.gutter_; + if (gutter !== 0) { + tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize); + tileExtent = ol.extent.buffer(tileExtent, + tileResolution * gutter, tileExtent); + } + + var baseParams = { + 'SERVICE': 'WMS', + 'VERSION': ol.DEFAULT_WMS_VERSION, + 'REQUEST': 'GetFeatureInfo', + 'FORMAT': 'image/png', + 'TRANSPARENT': true, + 'QUERY_LAYERS': this.params_['LAYERS'] + }; + ol.object.assign(baseParams, this.params_, params); + + var x = Math.floor((coordinate[0] - tileExtent[0]) / tileResolution); + var y = Math.floor((tileExtent[3] - coordinate[1]) / tileResolution); + + baseParams[this.v13_ ? 'I' : 'X'] = x; + baseParams[this.v13_ ? 'J' : 'Y'] = y; + + return this.getRequestUrl_(tileCoord, tileSize, tileExtent, + 1, projectionObj, baseParams); +}; + + +/** + * @inheritDoc + */ +ol.source.TileWMS.prototype.getGutterInternal = function() { + return this.gutter_; +}; + + +/** + * @inheritDoc + */ +ol.source.TileWMS.prototype.getKeyZXY = function(z, x, y) { + return this.coordKeyPrefix_ + ol.source.TileImage.prototype.getKeyZXY.call(this, z, x, y); +}; + + +/** + * Get the user-provided params, i.e. those passed to the constructor through + * the "params" option, and possibly updated using the updateParams method. + * @return {Object} Params. + * @api stable + */ +ol.source.TileWMS.prototype.getParams = function() { + return this.params_; +}; + + +/** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.Size} tileSize Tile size. + * @param {ol.Extent} tileExtent Tile extent. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @param {Object} params Params. + * @return {string|undefined} Request URL. + * @private + */ +ol.source.TileWMS.prototype.getRequestUrl_ = function(tileCoord, tileSize, tileExtent, + pixelRatio, projection, params) { + + var urls = this.urls; + if (!urls) { + return undefined; + } + + params['WIDTH'] = tileSize[0]; + params['HEIGHT'] = tileSize[1]; + + params[this.v13_ ? 'CRS' : 'SRS'] = projection.getCode(); + + if (!('STYLES' in this.params_)) { + params['STYLES'] = ''; + } + + if (pixelRatio != 1) { + switch (this.serverType_) { + case ol.source.wms.ServerType.GEOSERVER: + var dpi = (90 * pixelRatio + 0.5) | 0; + if ('FORMAT_OPTIONS' in params) { + params['FORMAT_OPTIONS'] += ';dpi:' + dpi; + } else { + params['FORMAT_OPTIONS'] = 'dpi:' + dpi; + } + break; + case ol.source.wms.ServerType.MAPSERVER: + params['MAP_RESOLUTION'] = 90 * pixelRatio; + break; + case ol.source.wms.ServerType.CARMENTA_SERVER: + case ol.source.wms.ServerType.QGIS: + params['DPI'] = 90 * pixelRatio; + break; + default: + goog.asserts.fail('unknown serverType configured'); + break; + } + } + + var axisOrientation = projection.getAxisOrientation(); + var bbox = tileExtent; + if (this.v13_ && axisOrientation.substr(0, 2) == 'ne') { + var tmp; + tmp = tileExtent[0]; + bbox[0] = tileExtent[1]; + bbox[1] = tmp; + tmp = tileExtent[2]; + bbox[2] = tileExtent[3]; + bbox[3] = tmp; + } + params['BBOX'] = bbox.join(','); + + var url; + if (urls.length == 1) { + url = urls[0]; + } else { + var index = ol.math.modulo(ol.tilecoord.hash(tileCoord), urls.length); + url = urls[index]; + } + return ol.uri.appendParams(url, params); +}; + + +/** + * @inheritDoc + */ +ol.source.TileWMS.prototype.getTilePixelRatio = function(pixelRatio) { + return (!this.hidpi_ || this.serverType_ === undefined) ? 1 : pixelRatio; +}; + + +/** + * @private + */ +ol.source.TileWMS.prototype.resetCoordKeyPrefix_ = function() { + var i = 0; + var res = []; + + if (this.urls) { + var j, jj; + for (j = 0, jj = this.urls.length; j < jj; ++j) { + res[i++] = this.urls[j]; + } + } + + this.coordKeyPrefix_ = res.join('#'); +}; + + +/** + * @private + * @return {string} The key for the current params. + */ +ol.source.TileWMS.prototype.getKeyForParams_ = function() { + var i = 0; + var res = []; + for (var key in this.params_) { + res[i++] = key + '-' + this.params_[key]; + } + return res.join('/'); +}; + + +/** + * @inheritDoc + */ +ol.source.TileWMS.prototype.fixedTileUrlFunction = function(tileCoord, pixelRatio, projection) { + + var tileGrid = this.getTileGrid(); + if (!tileGrid) { + tileGrid = this.getTileGridForProjection(projection); + } + + if (tileGrid.getResolutions().length <= tileCoord[0]) { + return undefined; + } + + if (pixelRatio != 1 && (!this.hidpi_ || this.serverType_ === undefined)) { + pixelRatio = 1; + } + + var tileResolution = tileGrid.getResolution(tileCoord[0]); + var tileExtent = tileGrid.getTileCoordExtent(tileCoord, this.tmpExtent_); + var tileSize = ol.size.toSize( + tileGrid.getTileSize(tileCoord[0]), this.tmpSize); + + var gutter = this.gutter_; + if (gutter !== 0) { + tileSize = ol.size.buffer(tileSize, gutter, this.tmpSize); + tileExtent = ol.extent.buffer(tileExtent, + tileResolution * gutter, tileExtent); + } + + if (pixelRatio != 1) { + tileSize = ol.size.scale(tileSize, pixelRatio, this.tmpSize); + } + + var baseParams = { + 'SERVICE': 'WMS', + 'VERSION': ol.DEFAULT_WMS_VERSION, + 'REQUEST': 'GetMap', + 'FORMAT': 'image/png', + 'TRANSPARENT': true + }; + ol.object.assign(baseParams, this.params_); + + return this.getRequestUrl_(tileCoord, tileSize, tileExtent, + pixelRatio, projection, baseParams); +}; + + +/** + * Update the user-provided params. + * @param {Object} params Params. + * @api stable + */ +ol.source.TileWMS.prototype.updateParams = function(params) { + ol.object.assign(this.params_, params); + this.resetCoordKeyPrefix_(); + this.updateV13_(); + this.setKey(this.getKeyForParams_()); +}; + + +/** + * @private + */ +ol.source.TileWMS.prototype.updateV13_ = function() { + var version = this.params_['VERSION'] || ol.DEFAULT_WMS_VERSION; + this.v13_ = ol.string.compareVersions(version, '1.3') >= 0; +}; + +goog.provide('ol.tilegrid.WMTS'); + +goog.require('goog.asserts'); +goog.require('ol.proj'); +goog.require('ol.tilegrid.TileGrid'); + + +/** + * @classdesc + * Set the grid pattern for sources accessing WMTS tiled-image servers. + * + * @constructor + * @extends {ol.tilegrid.TileGrid} + * @param {olx.tilegrid.WMTSOptions} options WMTS options. + * @struct + * @api + */ +ol.tilegrid.WMTS = function(options) { + + goog.asserts.assert( + options.resolutions.length == options.matrixIds.length, + 'options resolutions and matrixIds must have equal length (%s == %s)', + options.resolutions.length, options.matrixIds.length); + + /** + * @private + * @type {!Array.<string>} + */ + this.matrixIds_ = options.matrixIds; + // FIXME: should the matrixIds become optionnal? + + ol.tilegrid.TileGrid.call(this, { + extent: options.extent, + origin: options.origin, + origins: options.origins, + resolutions: options.resolutions, + tileSize: options.tileSize, + tileSizes: options.tileSizes, + sizes: options.sizes + }); + +}; +ol.inherits(ol.tilegrid.WMTS, ol.tilegrid.TileGrid); + + +/** + * @param {number} z Z. + * @return {string} MatrixId.. + */ +ol.tilegrid.WMTS.prototype.getMatrixId = function(z) { + goog.asserts.assert(0 <= z && z < this.matrixIds_.length, + 'attempted to retrive matrixId for illegal z (%s)', z); + return this.matrixIds_[z]; +}; + + +/** + * Get the list of matrix identifiers. + * @return {Array.<string>} MatrixIds. + * @api + */ +ol.tilegrid.WMTS.prototype.getMatrixIds = function() { + return this.matrixIds_; +}; + + +/** + * Create a tile grid from a WMTS capabilities matrix set. + * @param {Object} matrixSet An object representing a matrixSet in the + * capabilities document. + * @param {ol.Extent=} opt_extent An optional extent to restrict the tile + * ranges the server provides. + * @return {ol.tilegrid.WMTS} WMTS tileGrid instance. + * @api + */ +ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet = function(matrixSet, opt_extent) { + + /** @type {!Array.<number>} */ + var resolutions = []; + /** @type {!Array.<string>} */ + var matrixIds = []; + /** @type {!Array.<ol.Coordinate>} */ + var origins = []; + /** @type {!Array.<ol.Size>} */ + var tileSizes = []; + /** @type {!Array.<ol.Size>} */ + var sizes = []; + + var supportedCRSPropName = 'SupportedCRS'; + var matrixIdsPropName = 'TileMatrix'; + var identifierPropName = 'Identifier'; + var scaleDenominatorPropName = 'ScaleDenominator'; + var topLeftCornerPropName = 'TopLeftCorner'; + var tileWidthPropName = 'TileWidth'; + var tileHeightPropName = 'TileHeight'; + + var projection; + projection = ol.proj.get(matrixSet[supportedCRSPropName].replace( + /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')); + var metersPerUnit = projection.getMetersPerUnit(); + // swap origin x and y coordinates if axis orientation is lat/long + var switchOriginXY = projection.getAxisOrientation().substr(0, 2) == 'ne'; + + matrixSet[matrixIdsPropName].sort(function(a, b) { + return b[scaleDenominatorPropName] - a[scaleDenominatorPropName]; + }); + + matrixSet[matrixIdsPropName].forEach(function(elt, index, array) { + matrixIds.push(elt[identifierPropName]); + var resolution = elt[scaleDenominatorPropName] * 0.28E-3 / metersPerUnit; + var tileWidth = elt[tileWidthPropName]; + var tileHeight = elt[tileHeightPropName]; + if (switchOriginXY) { + origins.push([elt[topLeftCornerPropName][1], + elt[topLeftCornerPropName][0]]); + } else { + origins.push(elt[topLeftCornerPropName]); + } + resolutions.push(resolution); + tileSizes.push(tileWidth == tileHeight ? + tileWidth : [tileWidth, tileHeight]); + // top-left origin, so height is negative + sizes.push([elt['MatrixWidth'], -elt['MatrixHeight']]); + }); + + return new ol.tilegrid.WMTS({ + extent: opt_extent, + origins: origins, + resolutions: resolutions, + matrixIds: matrixIds, + tileSizes: tileSizes, + sizes: sizes + }); +}; + +goog.provide('ol.source.WMTS'); + +goog.require('goog.asserts'); +goog.require('ol.TileUrlFunction'); +goog.require('ol.array'); +goog.require('ol.extent'); +goog.require('ol.object'); +goog.require('ol.proj'); +goog.require('ol.source.TileImage'); +goog.require('ol.tilegrid.WMTS'); +goog.require('ol.uri'); + + +/** + * Request encoding. One of 'KVP', 'REST'. + * @enum {string} + */ +ol.source.WMTSRequestEncoding = { + KVP: 'KVP', // see spec §8 + REST: 'REST' // see spec §10 +}; + + +/** + * @classdesc + * Layer source for tile data from WMTS servers. + * + * @constructor + * @extends {ol.source.TileImage} + * @param {olx.source.WMTSOptions} options WMTS options. + * @api stable + */ +ol.source.WMTS = function(options) { + + // TODO: add support for TileMatrixLimits + + /** + * @private + * @type {string} + */ + this.version_ = options.version !== undefined ? options.version : '1.0.0'; + + /** + * @private + * @type {string} + */ + this.format_ = options.format !== undefined ? options.format : 'image/jpeg'; + + /** + * @private + * @type {!Object} + */ + this.dimensions_ = options.dimensions !== undefined ? options.dimensions : {}; + + /** + * @private + * @type {string} + */ + this.layer_ = options.layer; + + /** + * @private + * @type {string} + */ + this.matrixSet_ = options.matrixSet; + + /** + * @private + * @type {string} + */ + this.style_ = options.style; + + var urls = options.urls; + if (urls === undefined && options.url !== undefined) { + urls = ol.TileUrlFunction.expandUrl(options.url); + } + + // FIXME: should we guess this requestEncoding from options.url(s) + // structure? that would mean KVP only if a template is not provided. + + /** + * @private + * @type {ol.source.WMTSRequestEncoding} + */ + this.requestEncoding_ = options.requestEncoding !== undefined ? + /** @type {ol.source.WMTSRequestEncoding} */ (options.requestEncoding) : + ol.source.WMTSRequestEncoding.KVP; + + var requestEncoding = this.requestEncoding_; + + // FIXME: should we create a default tileGrid? + // we could issue a getCapabilities xhr to retrieve missing configuration + var tileGrid = options.tileGrid; + + // context property names are lower case to allow for a case insensitive + // replacement as some services use different naming conventions + var context = { + 'layer': this.layer_, + 'style': this.style_, + 'tilematrixset': this.matrixSet_ + }; + + if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) { + ol.object.assign(context, { + 'Service': 'WMTS', + 'Request': 'GetTile', + 'Version': this.version_, + 'Format': this.format_ + }); + } + + var dimensions = this.dimensions_; + + /** + * @param {string} template Template. + * @return {ol.TileUrlFunctionType} Tile URL function. + */ + function createFromWMTSTemplate(template) { + + // TODO: we may want to create our own appendParams function so that params + // order conforms to wmts spec guidance, and so that we can avoid to escape + // special template params + + template = (requestEncoding == ol.source.WMTSRequestEncoding.KVP) ? + ol.uri.appendParams(template, context) : + template.replace(/\{(\w+?)\}/g, function(m, p) { + return (p.toLowerCase() in context) ? context[p.toLowerCase()] : m; + }); + + return ( + /** + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; + } else { + var localContext = { + 'TileMatrix': tileGrid.getMatrixId(tileCoord[0]), + 'TileCol': tileCoord[1], + 'TileRow': -tileCoord[2] - 1 + }; + ol.object.assign(localContext, dimensions); + var url = template; + if (requestEncoding == ol.source.WMTSRequestEncoding.KVP) { + url = ol.uri.appendParams(url, localContext); + } else { + url = url.replace(/\{(\w+?)\}/g, function(m, p) { + return localContext[p]; + }); + } + return url; + } + }); + } + + var tileUrlFunction = (urls && urls.length > 0) ? + ol.TileUrlFunction.createFromTileUrlFunctions( + urls.map(createFromWMTSTemplate)) : + ol.TileUrlFunction.nullTileUrlFunction; + + ol.source.TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + logo: options.logo, + projection: options.projection, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileClass: options.tileClass, + tileGrid: tileGrid, + tileLoadFunction: options.tileLoadFunction, + tilePixelRatio: options.tilePixelRatio, + tileUrlFunction: tileUrlFunction, + urls: urls, + wrapX: options.wrapX !== undefined ? options.wrapX : false + }); + + this.setKey(this.getKeyForDimensions_()); + +}; +ol.inherits(ol.source.WMTS, ol.source.TileImage); + + +/** + * Get the dimensions, i.e. those passed to the constructor through the + * "dimensions" option, and possibly updated using the updateDimensions + * method. + * @return {!Object} Dimensions. + * @api + */ +ol.source.WMTS.prototype.getDimensions = function() { + return this.dimensions_; +}; + + +/** + * Return the image format of the WMTS source. + * @return {string} Format. + * @api + */ +ol.source.WMTS.prototype.getFormat = function() { + return this.format_; +}; + + +/** + * Return the layer of the WMTS source. + * @return {string} Layer. + * @api + */ +ol.source.WMTS.prototype.getLayer = function() { + return this.layer_; +}; + + +/** + * Return the matrix set of the WMTS source. + * @return {string} MatrixSet. + * @api + */ +ol.source.WMTS.prototype.getMatrixSet = function() { + return this.matrixSet_; +}; + + +/** + * Return the request encoding, either "KVP" or "REST". + * @return {ol.source.WMTSRequestEncoding} Request encoding. + * @api + */ +ol.source.WMTS.prototype.getRequestEncoding = function() { + return this.requestEncoding_; +}; + + +/** + * Return the style of the WMTS source. + * @return {string} Style. + * @api + */ +ol.source.WMTS.prototype.getStyle = function() { + return this.style_; +}; + + +/** + * Return the version of the WMTS source. + * @return {string} Version. + * @api + */ +ol.source.WMTS.prototype.getVersion = function() { + return this.version_; +}; + + +/** + * @private + * @return {string} The key for the current dimensions. + */ +ol.source.WMTS.prototype.getKeyForDimensions_ = function() { + var i = 0; + var res = []; + for (var key in this.dimensions_) { + res[i++] = key + '-' + this.dimensions_[key]; + } + return res.join('/'); +}; + + +/** + * Update the dimensions. + * @param {Object} dimensions Dimensions. + * @api + */ +ol.source.WMTS.prototype.updateDimensions = function(dimensions) { + ol.object.assign(this.dimensions_, dimensions); + this.setKey(this.getKeyForDimensions_()); +}; + + +/** + * Generate source options from a capabilities object. + * @param {Object} wmtsCap An object representing the capabilities document. + * @param {Object} config Configuration properties for the layer. Defaults for + * the layer will apply if not provided. + * + * Required config properties: + * - layer - {string} The layer identifier. + * + * Optional config properties: + * - matrixSet - {string} The matrix set identifier, required if there is + * more than one matrix set in the layer capabilities. + * - projection - {string} The desired CRS when no matrixSet is specified. + * eg: "EPSG:3857". If the desired projection is not available, + * an error is thrown. + * - requestEncoding - {string} url encoding format for the layer. Default is + * the first tile url format found in the GetCapabilities response. + * - style - {string} The name of the style + * - format - {string} Image format for the layer. Default is the first + * format returned in the GetCapabilities response. + * @return {olx.source.WMTSOptions} WMTS source options object. + * @api + */ +ol.source.WMTS.optionsFromCapabilities = function(wmtsCap, config) { + + // TODO: add support for TileMatrixLimits + goog.asserts.assert(config['layer'], + 'config "layer" must not be null'); + + var layers = wmtsCap['Contents']['Layer']; + var l = ol.array.find(layers, function(elt, index, array) { + return elt['Identifier'] == config['layer']; + }); + goog.asserts.assert(l, 'found a matching layer in Contents/Layer'); + + goog.asserts.assert(l['TileMatrixSetLink'].length > 0, + 'layer has TileMatrixSetLink'); + var tileMatrixSets = wmtsCap['Contents']['TileMatrixSet']; + var idx, matrixSet; + if (l['TileMatrixSetLink'].length > 1) { + if ('projection' in config) { + idx = ol.array.findIndex(l['TileMatrixSetLink'], + function(elt, index, array) { + var tileMatrixSet = ol.array.find(tileMatrixSets, function(el) { + return el['Identifier'] == elt['TileMatrixSet']; + }); + return tileMatrixSet['SupportedCRS'].replace( + /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3' + ) == config['projection']; + }); + } else { + idx = ol.array.findIndex(l['TileMatrixSetLink'], + function(elt, index, array) { + return elt['TileMatrixSet'] == config['matrixSet']; + }); + } + } else { + idx = 0; + } + if (idx < 0) { + idx = 0; + } + matrixSet = /** @type {string} */ + (l['TileMatrixSetLink'][idx]['TileMatrixSet']); + + goog.asserts.assert(matrixSet, 'TileMatrixSet must not be null'); + + var format = /** @type {string} */ (l['Format'][0]); + if ('format' in config) { + format = config['format']; + } + idx = ol.array.findIndex(l['Style'], function(elt, index, array) { + if ('style' in config) { + return elt['Title'] == config['style']; + } else { + return elt['isDefault']; + } + }); + if (idx < 0) { + idx = 0; + } + var style = /** @type {string} */ (l['Style'][idx]['Identifier']); + + var dimensions = {}; + if ('Dimension' in l) { + l['Dimension'].forEach(function(elt, index, array) { + var key = elt['Identifier']; + var value = elt['Default']; + if (value !== undefined) { + goog.asserts.assert(ol.array.includes(elt['Value'], value), + 'default value contained in values'); + } else { + value = elt['Value'][0]; + } + goog.asserts.assert(value !== undefined, 'value could be found'); + dimensions[key] = value; + }); + } + + var matrixSets = wmtsCap['Contents']['TileMatrixSet']; + var matrixSetObj = ol.array.find(matrixSets, function(elt, index, array) { + return elt['Identifier'] == matrixSet; + }); + goog.asserts.assert(matrixSetObj, + 'found matrixSet in Contents/TileMatrixSet'); + + var projection; + if ('projection' in config) { + projection = ol.proj.get(config['projection']); + } else { + projection = ol.proj.get(matrixSetObj['SupportedCRS'].replace( + /urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/, '$1:$3')); + } + + var wgs84BoundingBox = l['WGS84BoundingBox']; + var extent, wrapX; + if (wgs84BoundingBox !== undefined) { + var wgs84ProjectionExtent = ol.proj.get('EPSG:4326').getExtent(); + wrapX = (wgs84BoundingBox[0] == wgs84ProjectionExtent[0] && + wgs84BoundingBox[2] == wgs84ProjectionExtent[2]); + extent = ol.proj.transformExtent( + wgs84BoundingBox, 'EPSG:4326', projection); + var projectionExtent = projection.getExtent(); + if (projectionExtent) { + // If possible, do a sanity check on the extent - it should never be + // bigger than the validity extent of the projection of a matrix set. + if (!ol.extent.containsExtent(projectionExtent, extent)) { + extent = undefined; + } + } + } + + var tileGrid = ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet( + matrixSetObj, extent); + + /** @type {!Array.<string>} */ + var urls = []; + var requestEncoding = config['requestEncoding']; + requestEncoding = requestEncoding !== undefined ? requestEncoding : ''; + + goog.asserts.assert( + ol.array.includes(['REST', 'RESTful', 'KVP', ''], requestEncoding), + 'requestEncoding (%s) is one of "REST", "RESTful", "KVP" or ""', + requestEncoding); + + if ('OperationsMetadata' in wmtsCap && 'GetTile' in wmtsCap['OperationsMetadata']) { + var gets = wmtsCap['OperationsMetadata']['GetTile']['DCP']['HTTP']['Get']; + goog.asserts.assert(gets.length >= 1); + + for (var i = 0, ii = gets.length; i < ii; ++i) { + var constraint = ol.array.find(gets[i]['Constraint'], function(element) { + return element['name'] == 'GetEncoding'; + }); + var encodings = constraint['AllowedValues']['Value']; + goog.asserts.assert(encodings.length >= 1); + + if (requestEncoding === '') { + // requestEncoding not provided, use the first encoding from the list + requestEncoding = encodings[0]; + } + if (requestEncoding === ol.source.WMTSRequestEncoding.KVP) { + if (ol.array.includes(encodings, ol.source.WMTSRequestEncoding.KVP)) { + urls.push(/** @type {string} */ (gets[i]['href'])); + } + } else { + break; + } + } + } + if (urls.length === 0) { + requestEncoding = ol.source.WMTSRequestEncoding.REST; + l['ResourceURL'].forEach(function(element) { + if (element['resourceType'] === 'tile') { + format = element['format']; + urls.push(/** @type {string} */ (element['template'])); + } + }); + } + goog.asserts.assert(urls.length > 0, 'At least one URL found'); + + return { + urls: urls, + layer: config['layer'], + matrixSet: matrixSet, + format: format, + projection: projection, + requestEncoding: requestEncoding, + tileGrid: tileGrid, + style: style, + dimensions: dimensions, + wrapX: wrapX + }; + +}; + +goog.provide('ol.source.Zoomify'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.ImageTile'); +goog.require('ol.TileState'); +goog.require('ol.dom'); +goog.require('ol.extent'); +goog.require('ol.proj'); +goog.require('ol.source.TileImage'); +goog.require('ol.tilegrid.TileGrid'); + + +/** + * @enum {string} + */ +ol.source.ZoomifyTierSizeCalculation = { + DEFAULT: 'default', + TRUNCATED: 'truncated' +}; + + +/** + * @classdesc + * Layer source for tile data in Zoomify format. + * + * @constructor + * @extends {ol.source.TileImage} + * @param {olx.source.ZoomifyOptions=} opt_options Options. + * @api stable + */ +ol.source.Zoomify = function(opt_options) { + + var options = opt_options || {}; + + var size = options.size; + var tierSizeCalculation = options.tierSizeCalculation !== undefined ? + options.tierSizeCalculation : + ol.source.ZoomifyTierSizeCalculation.DEFAULT; + + var imageWidth = size[0]; + var imageHeight = size[1]; + var tierSizeInTiles = []; + var tileSize = ol.DEFAULT_TILE_SIZE; + + switch (tierSizeCalculation) { + case ol.source.ZoomifyTierSizeCalculation.DEFAULT: + while (imageWidth > tileSize || imageHeight > tileSize) { + tierSizeInTiles.push([ + Math.ceil(imageWidth / tileSize), + Math.ceil(imageHeight / tileSize) + ]); + tileSize += tileSize; + } + break; + case ol.source.ZoomifyTierSizeCalculation.TRUNCATED: + var width = imageWidth; + var height = imageHeight; + while (width > tileSize || height > tileSize) { + tierSizeInTiles.push([ + Math.ceil(width / tileSize), + Math.ceil(height / tileSize) + ]); + width >>= 1; + height >>= 1; + } + break; + default: + goog.asserts.fail(); + break; + } + + tierSizeInTiles.push([1, 1]); + tierSizeInTiles.reverse(); + + var resolutions = [1]; + var tileCountUpToTier = [0]; + var i, ii; + for (i = 1, ii = tierSizeInTiles.length; i < ii; i++) { + resolutions.push(1 << i); + tileCountUpToTier.push( + tierSizeInTiles[i - 1][0] * tierSizeInTiles[i - 1][1] + + tileCountUpToTier[i - 1] + ); + } + resolutions.reverse(); + + var extent = [0, -size[1], size[0], 0]; + var tileGrid = new ol.tilegrid.TileGrid({ + extent: extent, + origin: ol.extent.getTopLeft(extent), + resolutions: resolutions + }); + + var url = options.url; + + /** + * @this {ol.source.TileImage} + * @param {ol.TileCoord} tileCoord Tile Coordinate. + * @param {number} pixelRatio Pixel ratio. + * @param {ol.proj.Projection} projection Projection. + * @return {string|undefined} Tile URL. + */ + function tileUrlFunction(tileCoord, pixelRatio, projection) { + if (!tileCoord) { + return undefined; + } else { + var tileCoordZ = tileCoord[0]; + var tileCoordX = tileCoord[1]; + var tileCoordY = -tileCoord[2] - 1; + var tileIndex = + tileCoordX + + tileCoordY * tierSizeInTiles[tileCoordZ][0] + + tileCountUpToTier[tileCoordZ]; + var tileGroup = (tileIndex / ol.DEFAULT_TILE_SIZE) | 0; + return url + 'TileGroup' + tileGroup + '/' + + tileCoordZ + '-' + tileCoordX + '-' + tileCoordY + '.jpg'; + } + } + + ol.source.TileImage.call(this, { + attributions: options.attributions, + cacheSize: options.cacheSize, + crossOrigin: options.crossOrigin, + logo: options.logo, + reprojectionErrorThreshold: options.reprojectionErrorThreshold, + tileClass: ol.source.ZoomifyTile_, + tileGrid: tileGrid, + tileUrlFunction: tileUrlFunction + }); + +}; +ol.inherits(ol.source.Zoomify, ol.source.TileImage); + + +/** + * @constructor + * @extends {ol.ImageTile} + * @param {ol.TileCoord} tileCoord Tile coordinate. + * @param {ol.TileState} state State. + * @param {string} src Image source URI. + * @param {?string} crossOrigin Cross origin. + * @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function. + * @private + */ +ol.source.ZoomifyTile_ = function( + tileCoord, state, src, crossOrigin, tileLoadFunction) { + + ol.ImageTile.call(this, tileCoord, state, src, crossOrigin, tileLoadFunction); + + /** + * @private + * @type {Object.<string, + * HTMLCanvasElement|HTMLImageElement|HTMLVideoElement>} + */ + this.zoomifyImageByContext_ = {}; + +}; +ol.inherits(ol.source.ZoomifyTile_, ol.ImageTile); + + +/** + * @inheritDoc + */ +ol.source.ZoomifyTile_.prototype.getImage = function(opt_context) { + var tileSize = ol.DEFAULT_TILE_SIZE; + var key = opt_context !== undefined ? + goog.getUid(opt_context).toString() : ''; + if (key in this.zoomifyImageByContext_) { + return this.zoomifyImageByContext_[key]; + } else { + var image = ol.ImageTile.prototype.getImage.call(this, opt_context); + if (this.state == ol.TileState.LOADED) { + if (image.width == tileSize && image.height == tileSize) { + this.zoomifyImageByContext_[key] = image; + return image; + } else { + var context = ol.dom.createCanvasContext2D(tileSize, tileSize); + context.drawImage(image, 0, 0); + this.zoomifyImageByContext_[key] = context.canvas; + return context.canvas; + } + } else { + return image; + } + } +}; + +goog.provide('ol.style.Atlas'); +goog.provide('ol.style.AtlasManager'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.dom'); + + +/** + * Manages the creation of image atlases. + * + * Images added to this manager will be inserted into an atlas, which + * will be used for rendering. + * The `size` given in the constructor is the size for the first + * atlas. After that, when new atlases are created, they will have + * twice the size as the latest atlas (until `maxSize` is reached). + * + * If an application uses many images or very large images, it is recommended + * to set a higher `size` value to avoid the creation of too many atlases. + * + * @constructor + * @struct + * @api + * @param {olx.style.AtlasManagerOptions=} opt_options Options. + */ +ol.style.AtlasManager = function(opt_options) { + + var options = opt_options || {}; + + /** + * The size in pixels of the latest atlas image. + * @private + * @type {number} + */ + this.currentSize_ = options.initialSize !== undefined ? + options.initialSize : ol.INITIAL_ATLAS_SIZE; + + /** + * The maximum size in pixels of atlas images. + * @private + * @type {number} + */ + this.maxSize_ = options.maxSize !== undefined ? + options.maxSize : ol.MAX_ATLAS_SIZE != -1 ? + ol.MAX_ATLAS_SIZE : ol.WEBGL_MAX_TEXTURE_SIZE !== undefined ? + ol.WEBGL_MAX_TEXTURE_SIZE : 2048; + + /** + * The size in pixels between images. + * @private + * @type {number} + */ + this.space_ = options.space !== undefined ? options.space : 1; + + /** + * @private + * @type {Array.<ol.style.Atlas>} + */ + this.atlases_ = [new ol.style.Atlas(this.currentSize_, this.space_)]; + + /** + * The size in pixels of the latest atlas image for hit-detection images. + * @private + * @type {number} + */ + this.currentHitSize_ = this.currentSize_; + + /** + * @private + * @type {Array.<ol.style.Atlas>} + */ + this.hitAtlases_ = [new ol.style.Atlas(this.currentHitSize_, this.space_)]; +}; + + +/** + * @param {string} id The identifier of the entry to check. + * @return {?ol.AtlasManagerInfo} The position and atlas image for the + * entry, or `null` if the entry is not part of the atlas manager. + */ +ol.style.AtlasManager.prototype.getInfo = function(id) { + /** @type {?ol.AtlasInfo} */ + var info = this.getInfo_(this.atlases_, id); + + if (!info) { + return null; + } + /** @type {?ol.AtlasInfo} */ + var hitInfo = this.getInfo_(this.hitAtlases_, id); + goog.asserts.assert(hitInfo, 'hitInfo must not be null'); + + return this.mergeInfos_(info, hitInfo); +}; + + +/** + * @private + * @param {Array.<ol.style.Atlas>} atlases The atlases to search. + * @param {string} id The identifier of the entry to check. + * @return {?ol.AtlasInfo} The position and atlas image for the entry, + * or `null` if the entry is not part of the atlases. + */ +ol.style.AtlasManager.prototype.getInfo_ = function(atlases, id) { + var atlas, info, i, ii; + for (i = 0, ii = atlases.length; i < ii; ++i) { + atlas = atlases[i]; + info = atlas.get(id); + if (info) { + return info; + } + } + return null; +}; + + +/** + * @private + * @param {ol.AtlasInfo} info The info for the real image. + * @param {ol.AtlasInfo} hitInfo The info for the hit-detection + * image. + * @return {?ol.AtlasManagerInfo} The position and atlas image for the + * entry, or `null` if the entry is not part of the atlases. + */ +ol.style.AtlasManager.prototype.mergeInfos_ = function(info, hitInfo) { + goog.asserts.assert(info.offsetX === hitInfo.offsetX, + 'in order to merge, offsetX of info and hitInfo must be equal'); + goog.asserts.assert(info.offsetY === hitInfo.offsetY, + 'in order to merge, offsetY of info and hitInfo must be equal'); + return /** @type {ol.AtlasManagerInfo} */ ({ + offsetX: info.offsetX, + offsetY: info.offsetY, + image: info.image, + hitImage: hitInfo.image + }); +}; + + +/** + * Add an image to the atlas manager. + * + * If an entry for the given id already exists, the entry will + * be overridden (but the space on the atlas graphic will not be freed). + * + * If `renderHitCallback` is provided, the image (or the hit-detection version + * of the image) will be rendered into a separate hit-detection atlas image. + * + * @param {string} id The identifier of the entry to add. + * @param {number} width The width. + * @param {number} height The height. + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback + * Called to render the new image onto an atlas image. + * @param {function(CanvasRenderingContext2D, number, number)=} + * opt_renderHitCallback Called to render a hit-detection image onto a hit + * detection atlas image. + * @param {Object=} opt_this Value to use as `this` when executing + * `renderCallback` and `renderHitCallback`. + * @return {?ol.AtlasManagerInfo} The position and atlas image for the + * entry, or `null` if the image is too big. + */ +ol.style.AtlasManager.prototype.add = function(id, width, height, + renderCallback, opt_renderHitCallback, opt_this) { + if (width + this.space_ > this.maxSize_ || + height + this.space_ > this.maxSize_) { + return null; + } + + /** @type {?ol.AtlasInfo} */ + var info = this.add_(false, + id, width, height, renderCallback, opt_this); + if (!info) { + return null; + } + + // even if no hit-detection entry is requested, we insert a fake entry into + // the hit-detection atlas, to make sure that the offset is the same for + // the original image and the hit-detection image. + var renderHitCallback = opt_renderHitCallback !== undefined ? + opt_renderHitCallback : ol.nullFunction; + + /** @type {?ol.AtlasInfo} */ + var hitInfo = this.add_(true, + id, width, height, renderHitCallback, opt_this); + goog.asserts.assert(hitInfo, 'hitInfo must not be null'); + + return this.mergeInfos_(info, hitInfo); +}; + + +/** + * @private + * @param {boolean} isHitAtlas If the hit-detection atlases are used. + * @param {string} id The identifier of the entry to add. + * @param {number} width The width. + * @param {number} height The height. + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback + * Called to render the new image onto an atlas image. + * @param {Object=} opt_this Value to use as `this` when executing + * `renderCallback` and `renderHitCallback`. + * @return {?ol.AtlasInfo} The position and atlas image for the entry, + * or `null` if the image is too big. + */ +ol.style.AtlasManager.prototype.add_ = function(isHitAtlas, id, width, height, + renderCallback, opt_this) { + var atlases = (isHitAtlas) ? this.hitAtlases_ : this.atlases_; + var atlas, info, i, ii; + for (i = 0, ii = atlases.length; i < ii; ++i) { + atlas = atlases[i]; + info = atlas.add(id, width, height, renderCallback, opt_this); + if (info) { + return info; + } else if (!info && i === ii - 1) { + // the entry could not be added to one of the existing atlases, + // create a new atlas that is twice as big and try to add to this one. + var size; + if (isHitAtlas) { + size = Math.min(this.currentHitSize_ * 2, this.maxSize_); + this.currentHitSize_ = size; + } else { + size = Math.min(this.currentSize_ * 2, this.maxSize_); + this.currentSize_ = size; + } + atlas = new ol.style.Atlas(size, this.space_); + atlases.push(atlas); + // run the loop another time + ++ii; + } + } + goog.asserts.fail('Failed to add to atlasmanager'); +}; + + +/** + * This class facilitates the creation of image atlases. + * + * Images added to an atlas will be rendered onto a single + * atlas canvas. The distribution of images on the canvas is + * managed with the bin packing algorithm described in: + * http://www.blackpawn.com/texts/lightmaps/ + * + * @constructor + * @struct + * @param {number} size The size in pixels of the sprite image. + * @param {number} space The space in pixels between images. + * Because texture coordinates are float values, the edges of + * images might not be completely correct (in a way that the + * edges overlap when being rendered). To avoid this we add a + * padding around each image. + */ +ol.style.Atlas = function(size, space) { + + /** + * @private + * @type {number} + */ + this.space_ = space; + + /** + * @private + * @type {Array.<ol.AtlasBlock>} + */ + this.emptyBlocks_ = [{x: 0, y: 0, width: size, height: size}]; + + /** + * @private + * @type {Object.<string, ol.AtlasInfo>} + */ + this.entries_ = {}; + + /** + * @private + * @type {CanvasRenderingContext2D} + */ + this.context_ = ol.dom.createCanvasContext2D(size, size); + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = this.context_.canvas; +}; + + +/** + * @param {string} id The identifier of the entry to check. + * @return {?ol.AtlasInfo} The atlas info. + */ +ol.style.Atlas.prototype.get = function(id) { + return this.entries_[id] || null; +}; + + +/** + * @param {string} id The identifier of the entry to add. + * @param {number} width The width. + * @param {number} height The height. + * @param {function(CanvasRenderingContext2D, number, number)} renderCallback + * Called to render the new image onto an atlas image. + * @param {Object=} opt_this Value to use as `this` when executing + * `renderCallback`. + * @return {?ol.AtlasInfo} The position and atlas image for the entry. + */ +ol.style.Atlas.prototype.add = function(id, width, height, renderCallback, opt_this) { + var block, i, ii; + for (i = 0, ii = this.emptyBlocks_.length; i < ii; ++i) { + block = this.emptyBlocks_[i]; + if (block.width >= width + this.space_ && + block.height >= height + this.space_) { + // we found a block that is big enough for our entry + var entry = { + offsetX: block.x + this.space_, + offsetY: block.y + this.space_, + image: this.canvas_ + }; + this.entries_[id] = entry; + + // render the image on the atlas image + renderCallback.call(opt_this, this.context_, + block.x + this.space_, block.y + this.space_); + + // split the block after the insertion, either horizontally or vertically + this.split_(i, block, width + this.space_, height + this.space_); + + return entry; + } + } + + // there is no space for the new entry in this atlas + return null; +}; + + +/** + * @private + * @param {number} index The index of the block. + * @param {ol.AtlasBlock} block The block to split. + * @param {number} width The width of the entry to insert. + * @param {number} height The height of the entry to insert. + */ +ol.style.Atlas.prototype.split_ = function(index, block, width, height) { + var deltaWidth = block.width - width; + var deltaHeight = block.height - height; + + /** @type {ol.AtlasBlock} */ + var newBlock1; + /** @type {ol.AtlasBlock} */ + var newBlock2; + + if (deltaWidth > deltaHeight) { + // split vertically + // block right of the inserted entry + newBlock1 = { + x: block.x + width, + y: block.y, + width: block.width - width, + height: block.height + }; + + // block below the inserted entry + newBlock2 = { + x: block.x, + y: block.y + height, + width: width, + height: block.height - height + }; + this.updateBlocks_(index, newBlock1, newBlock2); + } else { + // split horizontally + // block right of the inserted entry + newBlock1 = { + x: block.x + width, + y: block.y, + width: block.width - width, + height: height + }; + + // block below the inserted entry + newBlock2 = { + x: block.x, + y: block.y + height, + width: block.width, + height: block.height - height + }; + this.updateBlocks_(index, newBlock1, newBlock2); + } +}; + + +/** + * Remove the old block and insert new blocks at the same array position. + * The new blocks are inserted at the same position, so that splitted + * blocks (that are potentially smaller) are filled first. + * @private + * @param {number} index The index of the block to remove. + * @param {ol.AtlasBlock} newBlock1 The 1st block to add. + * @param {ol.AtlasBlock} newBlock2 The 2nd block to add. + */ +ol.style.Atlas.prototype.updateBlocks_ = function(index, newBlock1, newBlock2) { + var args = [index, 1]; + if (newBlock1.width > 0 && newBlock1.height > 0) { + args.push(newBlock1); + } + if (newBlock2.width > 0 && newBlock2.height > 0) { + args.push(newBlock2); + } + this.emptyBlocks_.splice.apply(this.emptyBlocks_, args); +}; + +goog.provide('ol.style.RegularShape'); + +goog.require('goog.asserts'); +goog.require('ol'); +goog.require('ol.color'); +goog.require('ol.colorlike'); +goog.require('ol.dom'); +goog.require('ol.has'); +goog.require('ol.render.canvas'); +goog.require('ol.style.AtlasManager'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Image'); +goog.require('ol.style.ImageState'); +goog.require('ol.style.Stroke'); + + +/** + * @classdesc + * Set regular shape style for vector features. The resulting shape will be + * a regular polygon when `radius` is provided, or a star when `radius1` and + * `radius2` are provided. + * + * @constructor + * @param {olx.style.RegularShapeOptions} options Options. + * @extends {ol.style.Image} + * @api + */ +ol.style.RegularShape = function(options) { + + goog.asserts.assert( + options.radius !== undefined || options.radius1 !== undefined, + 'must provide either "radius" or "radius1"'); + + /** + * @private + * @type {Array.<string>} + */ + this.checksums_ = null; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.canvas_ = null; + + /** + * @private + * @type {HTMLCanvasElement} + */ + this.hitDetectionCanvas_ = null; + + /** + * @private + * @type {ol.style.Fill} + */ + this.fill_ = options.fill !== undefined ? options.fill : null; + + /** + * @private + * @type {Array.<number>} + */ + this.origin_ = [0, 0]; + + /** + * @private + * @type {number} + */ + this.points_ = options.points; + + /** + * @private + * @type {number} + */ + this.radius_ = /** @type {number} */ (options.radius !== undefined ? + options.radius : options.radius1); + + /** + * @private + * @type {number} + */ + this.radius2_ = + options.radius2 !== undefined ? options.radius2 : this.radius_; + + /** + * @private + * @type {number} + */ + this.angle_ = options.angle !== undefined ? options.angle : 0; + + /** + * @private + * @type {ol.style.Stroke} + */ + this.stroke_ = options.stroke !== undefined ? options.stroke : null; + + /** + * @private + * @type {Array.<number>} + */ + this.anchor_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.size_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.imageSize_ = null; + + /** + * @private + * @type {ol.Size} + */ + this.hitDetectionImageSize_ = null; + + this.render_(options.atlasManager); + + /** + * @type {boolean} + */ + var snapToPixel = options.snapToPixel !== undefined ? + options.snapToPixel : true; + + /** + * @type {boolean} + */ + var rotateWithView = options.rotateWithView !== undefined ? + options.rotateWithView : false; + + ol.style.Image.call(this, { + opacity: 1, + rotateWithView: rotateWithView, + rotation: options.rotation !== undefined ? options.rotation : 0, + scale: 1, + snapToPixel: snapToPixel + }); + +}; +ol.inherits(ol.style.RegularShape, ol.style.Image); + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getAnchor = function() { + return this.anchor_; +}; + + +/** + * Get the angle used in generating the shape. + * @return {number} Shape's rotation in radians. + * @api + */ +ol.style.RegularShape.prototype.getAngle = function() { + return this.angle_; +}; + + +/** + * Get the fill style for the shape. + * @return {ol.style.Fill} Fill style. + * @api + */ +ol.style.RegularShape.prototype.getFill = function() { + return this.fill_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.getHitDetectionImage = function(pixelRatio) { + return this.hitDetectionCanvas_; +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getImage = function(pixelRatio) { + return this.canvas_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.getImageSize = function() { + return this.imageSize_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.getHitDetectionImageSize = function() { + return this.hitDetectionImageSize_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.getImageState = function() { + return ol.style.ImageState.LOADED; +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getOrigin = function() { + return this.origin_; +}; + + +/** + * Get the number of points for generating the shape. + * @return {number} Number of points for stars and regular polygons. + * @api + */ +ol.style.RegularShape.prototype.getPoints = function() { + return this.points_; +}; + + +/** + * Get the (primary) radius for the shape. + * @return {number} Radius. + * @api + */ +ol.style.RegularShape.prototype.getRadius = function() { + return this.radius_; +}; + + +/** + * Get the secondary radius for the shape. + * @return {number} Radius2. + * @api + */ +ol.style.RegularShape.prototype.getRadius2 = function() { + return this.radius2_; +}; + + +/** + * @inheritDoc + * @api + */ +ol.style.RegularShape.prototype.getSize = function() { + return this.size_; +}; + + +/** + * Get the stroke style for the shape. + * @return {ol.style.Stroke} Stroke style. + * @api + */ +ol.style.RegularShape.prototype.getStroke = function() { + return this.stroke_; +}; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.listenImageChange = ol.nullFunction; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.load = ol.nullFunction; + + +/** + * @inheritDoc + */ +ol.style.RegularShape.prototype.unlistenImageChange = ol.nullFunction; + + +/** + * @private + * @param {ol.style.AtlasManager|undefined} atlasManager An atlas manager. + */ +ol.style.RegularShape.prototype.render_ = function(atlasManager) { + var imageSize; + var lineCap = ''; + var lineJoin = ''; + var miterLimit = 0; + var lineDash = null; + var strokeStyle; + var strokeWidth = 0; + + if (this.stroke_) { + strokeStyle = ol.color.asString(this.stroke_.getColor()); + strokeWidth = this.stroke_.getWidth(); + if (strokeWidth === undefined) { + strokeWidth = ol.render.canvas.defaultLineWidth; + } + lineDash = this.stroke_.getLineDash(); + if (!ol.has.CANVAS_LINE_DASH) { + lineDash = null; + } + lineJoin = this.stroke_.getLineJoin(); + if (lineJoin === undefined) { + lineJoin = ol.render.canvas.defaultLineJoin; + } + lineCap = this.stroke_.getLineCap(); + if (lineCap === undefined) { + lineCap = ol.render.canvas.defaultLineCap; + } + miterLimit = this.stroke_.getMiterLimit(); + if (miterLimit === undefined) { + miterLimit = ol.render.canvas.defaultMiterLimit; + } + } + + var size = 2 * (this.radius_ + strokeWidth) + 1; + + /** @type {ol.RegularShapeRenderOptions} */ + var renderOptions = { + strokeStyle: strokeStyle, + strokeWidth: strokeWidth, + size: size, + lineCap: lineCap, + lineDash: lineDash, + lineJoin: lineJoin, + miterLimit: miterLimit + }; + + if (atlasManager === undefined) { + // no atlas manager is used, create a new canvas + var context = ol.dom.createCanvasContext2D(size, size); + this.canvas_ = context.canvas; + + // canvas.width and height are rounded to the closest integer + size = this.canvas_.width; + imageSize = size; + + this.draw_(renderOptions, context, 0, 0); + + this.createHitDetectionCanvas_(renderOptions); + } else { + // an atlas manager is used, add the symbol to an atlas + size = Math.round(size); + + var hasCustomHitDetectionImage = !this.fill_; + var renderHitDetectionCallback; + if (hasCustomHitDetectionImage) { + // render the hit-detection image into a separate atlas image + renderHitDetectionCallback = + this.drawHitDetectionCanvas_.bind(this, renderOptions); + } + + var id = this.getChecksum(); + var info = atlasManager.add( + id, size, size, this.draw_.bind(this, renderOptions), + renderHitDetectionCallback); + goog.asserts.assert(info, 'shape size is too large'); + + this.canvas_ = info.image; + this.origin_ = [info.offsetX, info.offsetY]; + imageSize = info.image.width; + + if (hasCustomHitDetectionImage) { + this.hitDetectionCanvas_ = info.hitImage; + this.hitDetectionImageSize_ = + [info.hitImage.width, info.hitImage.height]; + } else { + this.hitDetectionCanvas_ = this.canvas_; + this.hitDetectionImageSize_ = [imageSize, imageSize]; + } + } + + this.anchor_ = [size / 2, size / 2]; + this.size_ = [size, size]; + this.imageSize_ = [imageSize, imageSize]; +}; + + +/** + * @private + * @param {ol.RegularShapeRenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The rendering context. + * @param {number} x The origin for the symbol (x). + * @param {number} y The origin for the symbol (y). + */ +ol.style.RegularShape.prototype.draw_ = function(renderOptions, context, x, y) { + var i, angle0, radiusC; + // reset transform + context.setTransform(1, 0, 0, 1, 0, 0); + + // then move to (x, y) + context.translate(x, y); + + context.beginPath(); + if (this.radius2_ !== this.radius_) { + this.points_ = 2 * this.points_; + } + for (i = 0; i <= this.points_; i++) { + angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_; + radiusC = i % 2 === 0 ? this.radius_ : this.radius2_; + context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), + renderOptions.size / 2 + radiusC * Math.sin(angle0)); + } + + if (this.fill_) { + context.fillStyle = ol.colorlike.asColorLike(this.fill_.getColor()); + context.fill(); + } + if (this.stroke_) { + context.strokeStyle = renderOptions.strokeStyle; + context.lineWidth = renderOptions.strokeWidth; + if (renderOptions.lineDash) { + context.setLineDash(renderOptions.lineDash); + } + context.lineCap = renderOptions.lineCap; + context.lineJoin = renderOptions.lineJoin; + context.miterLimit = renderOptions.miterLimit; + context.stroke(); + } + context.closePath(); +}; + + +/** + * @private + * @param {ol.RegularShapeRenderOptions} renderOptions Render options. + */ +ol.style.RegularShape.prototype.createHitDetectionCanvas_ = function(renderOptions) { + this.hitDetectionImageSize_ = [renderOptions.size, renderOptions.size]; + if (this.fill_) { + this.hitDetectionCanvas_ = this.canvas_; + return; + } + + // if no fill style is set, create an extra hit-detection image with a + // default fill style + var context = ol.dom.createCanvasContext2D(renderOptions.size, renderOptions.size); + this.hitDetectionCanvas_ = context.canvas; + + this.drawHitDetectionCanvas_(renderOptions, context, 0, 0); +}; + + +/** + * @private + * @param {ol.RegularShapeRenderOptions} renderOptions Render options. + * @param {CanvasRenderingContext2D} context The context. + * @param {number} x The origin for the symbol (x). + * @param {number} y The origin for the symbol (y). + */ +ol.style.RegularShape.prototype.drawHitDetectionCanvas_ = function(renderOptions, context, x, y) { + // reset transform + context.setTransform(1, 0, 0, 1, 0, 0); + + // then move to (x, y) + context.translate(x, y); + + context.beginPath(); + if (this.radius2_ !== this.radius_) { + this.points_ = 2 * this.points_; + } + var i, radiusC, angle0; + for (i = 0; i <= this.points_; i++) { + angle0 = i * 2 * Math.PI / this.points_ - Math.PI / 2 + this.angle_; + radiusC = i % 2 === 0 ? this.radius_ : this.radius2_; + context.lineTo(renderOptions.size / 2 + radiusC * Math.cos(angle0), + renderOptions.size / 2 + radiusC * Math.sin(angle0)); + } + + context.fillStyle = ol.render.canvas.defaultFillStyle; + context.fill(); + if (this.stroke_) { + context.strokeStyle = renderOptions.strokeStyle; + context.lineWidth = renderOptions.strokeWidth; + if (renderOptions.lineDash) { + context.setLineDash(renderOptions.lineDash); + } + context.stroke(); + } + context.closePath(); +}; + + +/** + * @return {string} The checksum. + */ +ol.style.RegularShape.prototype.getChecksum = function() { + var strokeChecksum = this.stroke_ ? + this.stroke_.getChecksum() : '-'; + var fillChecksum = this.fill_ ? + this.fill_.getChecksum() : '-'; + + var recalculate = !this.checksums_ || + (strokeChecksum != this.checksums_[1] || + fillChecksum != this.checksums_[2] || + this.radius_ != this.checksums_[3] || + this.radius2_ != this.checksums_[4] || + this.angle_ != this.checksums_[5] || + this.points_ != this.checksums_[6]); + + if (recalculate) { + var checksum = 'r' + strokeChecksum + fillChecksum + + (this.radius_ !== undefined ? this.radius_.toString() : '-') + + (this.radius2_ !== undefined ? this.radius2_.toString() : '-') + + (this.angle_ !== undefined ? this.angle_.toString() : '-') + + (this.points_ !== undefined ? this.points_.toString() : '-'); + this.checksums_ = [checksum, strokeChecksum, fillChecksum, + this.radius_, this.radius2_, this.angle_, this.points_]; + } + + return this.checksums_[0]; +}; + +// Copyright 2009 The Closure Library Authors. +// All Rights Reserved. +// +// 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 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This file has been auto-generated by GenJsDeps, please do not edit. + +goog.addDependency( + 'demos/editor/equationeditor.js', ['goog.demos.editor.EquationEditor'], + ['goog.ui.equation.EquationEditorDialog']); +goog.addDependency( + 'demos/editor/helloworld.js', ['goog.demos.editor.HelloWorld'], + ['goog.dom', 'goog.dom.TagName', 'goog.editor.Plugin']); +goog.addDependency( + 'demos/editor/helloworlddialog.js', + [ + 'goog.demos.editor.HelloWorldDialog', + 'goog.demos.editor.HelloWorldDialog.OkEvent' + ], + [ + 'goog.dom.TagName', 'goog.events.Event', 'goog.string', + 'goog.ui.editor.AbstractDialog', 'goog.ui.editor.AbstractDialog.Builder', + 'goog.ui.editor.AbstractDialog.EventType' + ]); +goog.addDependency( + 'demos/editor/helloworlddialogplugin.js', + [ + 'goog.demos.editor.HelloWorldDialogPlugin', + 'goog.demos.editor.HelloWorldDialogPlugin.Command' + ], + [ + 'goog.demos.editor.HelloWorldDialog', 'goog.dom.TagName', + 'goog.editor.plugins.AbstractDialogPlugin', 'goog.editor.range', + 'goog.functions', 'goog.ui.editor.AbstractDialog.EventType' + ]); + +/** + * @fileoverview Custom exports file. + * @suppress {checkVars,extraRequire} + */ + +goog.require('ol'); +goog.require('ol.Attribution'); +goog.require('ol.Collection'); +goog.require('ol.CollectionEvent'); +goog.require('ol.CollectionEventType'); +goog.require('ol.DeviceOrientation'); +goog.require('ol.DragBoxEvent'); +goog.require('ol.Feature'); +goog.require('ol.Geolocation'); +goog.require('ol.Graticule'); +goog.require('ol.Image'); +goog.require('ol.ImageTile'); +goog.require('ol.Kinetic'); +goog.require('ol.Map'); +goog.require('ol.MapBrowserEvent'); +goog.require('ol.MapBrowserEvent.EventType'); +goog.require('ol.MapBrowserEventHandler'); +goog.require('ol.MapBrowserPointerEvent'); +goog.require('ol.MapEvent'); +goog.require('ol.MapEventType'); +goog.require('ol.MapProperty'); +goog.require('ol.Object'); +goog.require('ol.ObjectEvent'); +goog.require('ol.ObjectEventType'); +goog.require('ol.Observable'); +goog.require('ol.Overlay'); +goog.require('ol.OverlayPositioning'); +goog.require('ol.RasterOperationType'); +goog.require('ol.Sphere'); +goog.require('ol.Tile'); +goog.require('ol.TileState'); +goog.require('ol.VectorTile'); +goog.require('ol.View'); +goog.require('ol.ViewHint'); +goog.require('ol.ViewProperty'); +goog.require('ol.animation'); +goog.require('ol.color'); +goog.require('ol.colorlike'); +goog.require('ol.control'); +goog.require('ol.control.Attribution'); +goog.require('ol.control.Control'); +goog.require('ol.control.FullScreen'); +goog.require('ol.control.MousePosition'); +goog.require('ol.control.OverviewMap'); +goog.require('ol.control.Rotate'); +goog.require('ol.control.ScaleLine'); +goog.require('ol.control.Zoom'); +goog.require('ol.control.ZoomSlider'); +goog.require('ol.control.ZoomToExtent'); +goog.require('ol.coordinate'); +goog.require('ol.easing'); +goog.require('ol.events.Event'); +goog.require('ol.events.condition'); +goog.require('ol.extent'); +goog.require('ol.extent.Corner'); +goog.require('ol.extent.Relationship'); +goog.require('ol.featureloader'); +goog.require('ol.format.EsriJSON'); +goog.require('ol.format.Feature'); +goog.require('ol.format.GML'); +goog.require('ol.format.GML2'); +goog.require('ol.format.GML3'); +goog.require('ol.format.GMLBase'); +goog.require('ol.format.GPX'); +goog.require('ol.format.GeoJSON'); +goog.require('ol.format.IGC'); +goog.require('ol.format.KML'); +goog.require('ol.format.MVT'); +goog.require('ol.format.OSMXML'); +goog.require('ol.format.Polyline'); +goog.require('ol.format.TopoJSON'); +goog.require('ol.format.WFS'); +goog.require('ol.format.WKT'); +goog.require('ol.format.WMSCapabilities'); +goog.require('ol.format.WMSGetFeatureInfo'); +goog.require('ol.format.WMTSCapabilities'); +goog.require('ol.format.ogc.filter'); +goog.require('ol.format.ogc.filter.And'); +goog.require('ol.format.ogc.filter.Bbox'); +goog.require('ol.format.ogc.filter.Comparison'); +goog.require('ol.format.ogc.filter.ComparisonBinary'); +goog.require('ol.format.ogc.filter.EqualTo'); +goog.require('ol.format.ogc.filter.Filter'); +goog.require('ol.format.ogc.filter.GreaterThan'); +goog.require('ol.format.ogc.filter.GreaterThanOrEqualTo'); +goog.require('ol.format.ogc.filter.IsBetween'); +goog.require('ol.format.ogc.filter.IsLike'); +goog.require('ol.format.ogc.filter.IsNull'); +goog.require('ol.format.ogc.filter.LessThan'); +goog.require('ol.format.ogc.filter.LessThanOrEqualTo'); +goog.require('ol.format.ogc.filter.Logical'); +goog.require('ol.format.ogc.filter.LogicalBinary'); +goog.require('ol.format.ogc.filter.Not'); +goog.require('ol.format.ogc.filter.NotEqualTo'); +goog.require('ol.format.ogc.filter.Or'); +goog.require('ol.geom.Circle'); +goog.require('ol.geom.Geometry'); +goog.require('ol.geom.GeometryCollection'); +goog.require('ol.geom.GeometryLayout'); +goog.require('ol.geom.GeometryType'); +goog.require('ol.geom.LineString'); +goog.require('ol.geom.LinearRing'); +goog.require('ol.geom.MultiLineString'); +goog.require('ol.geom.MultiPoint'); +goog.require('ol.geom.MultiPolygon'); +goog.require('ol.geom.Point'); +goog.require('ol.geom.Polygon'); +goog.require('ol.geom.SimpleGeometry'); +goog.require('ol.has'); +goog.require('ol.interaction'); +goog.require('ol.interaction.DoubleClickZoom'); +goog.require('ol.interaction.DragAndDrop'); +goog.require('ol.interaction.DragAndDropEvent'); +goog.require('ol.interaction.DragBox'); +goog.require('ol.interaction.DragPan'); +goog.require('ol.interaction.DragRotate'); +goog.require('ol.interaction.DragRotateAndZoom'); +goog.require('ol.interaction.DragZoom'); +goog.require('ol.interaction.Draw'); +goog.require('ol.interaction.DrawEvent'); +goog.require('ol.interaction.DrawEventType'); +goog.require('ol.interaction.DrawMode'); +goog.require('ol.interaction.Interaction'); +goog.require('ol.interaction.InteractionProperty'); +goog.require('ol.interaction.KeyboardPan'); +goog.require('ol.interaction.KeyboardZoom'); +goog.require('ol.interaction.Modify'); +goog.require('ol.interaction.ModifyEvent'); +goog.require('ol.interaction.MouseWheelZoom'); +goog.require('ol.interaction.PinchRotate'); +goog.require('ol.interaction.PinchZoom'); +goog.require('ol.interaction.Pointer'); +goog.require('ol.interaction.Select'); +goog.require('ol.interaction.SelectEvent'); +goog.require('ol.interaction.SelectEventType'); +goog.require('ol.interaction.Snap'); +goog.require('ol.interaction.SnapProperty'); +goog.require('ol.interaction.Translate'); +goog.require('ol.interaction.TranslateEvent'); +goog.require('ol.layer.Base'); +goog.require('ol.layer.Group'); +goog.require('ol.layer.Heatmap'); +goog.require('ol.layer.Image'); +goog.require('ol.layer.Layer'); +goog.require('ol.layer.LayerProperty'); +goog.require('ol.layer.Tile'); +goog.require('ol.layer.Vector'); +goog.require('ol.layer.VectorTile'); +goog.require('ol.loadingstrategy'); +goog.require('ol.proj'); +goog.require('ol.proj.METERS_PER_UNIT'); +goog.require('ol.proj.Projection'); +goog.require('ol.proj.Units'); +goog.require('ol.proj.common'); +goog.require('ol.render'); +goog.require('ol.render.Event'); +goog.require('ol.render.EventType'); +goog.require('ol.render.Feature'); +goog.require('ol.render.VectorContext'); +goog.require('ol.render.canvas.Immediate'); +goog.require('ol.render.webgl.Immediate'); +goog.require('ol.size'); +goog.require('ol.source.BingMaps'); +goog.require('ol.source.CartoDB'); +goog.require('ol.source.Cluster'); +goog.require('ol.source.Image'); +goog.require('ol.source.ImageArcGISRest'); +goog.require('ol.source.ImageCanvas'); +goog.require('ol.source.ImageEvent'); +goog.require('ol.source.ImageMapGuide'); +goog.require('ol.source.ImageStatic'); +goog.require('ol.source.ImageVector'); +goog.require('ol.source.ImageWMS'); +goog.require('ol.source.OSM'); +goog.require('ol.source.Raster'); +goog.require('ol.source.RasterEvent'); +goog.require('ol.source.RasterEventType'); +goog.require('ol.source.Source'); +goog.require('ol.source.Stamen'); +goog.require('ol.source.State'); +goog.require('ol.source.Tile'); +goog.require('ol.source.TileArcGISRest'); +goog.require('ol.source.TileDebug'); +goog.require('ol.source.TileEvent'); +goog.require('ol.source.TileImage'); +goog.require('ol.source.TileJSON'); +goog.require('ol.source.TileUTFGrid'); +goog.require('ol.source.TileWMS'); +goog.require('ol.source.UrlTile'); +goog.require('ol.source.Vector'); +goog.require('ol.source.VectorEvent'); +goog.require('ol.source.VectorEventType'); +goog.require('ol.source.VectorTile'); +goog.require('ol.source.WMTS'); +goog.require('ol.source.XYZ'); +goog.require('ol.source.Zoomify'); +goog.require('ol.style.Atlas'); +goog.require('ol.style.AtlasManager'); +goog.require('ol.style.Circle'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Icon'); +goog.require('ol.style.IconAnchorUnits'); +goog.require('ol.style.IconImageCache'); +goog.require('ol.style.IconOrigin'); +goog.require('ol.style.Image'); +goog.require('ol.style.ImageState'); +goog.require('ol.style.RegularShape'); +goog.require('ol.style.Stroke'); +goog.require('ol.style.Style'); +goog.require('ol.style.Text'); +goog.require('ol.style.defaultGeometryFunction'); +goog.require('ol.tilegrid.TileGrid'); +goog.require('ol.tilegrid.WMTS'); +goog.require('ol.tilejson'); +goog.require('ol.webgl.Context'); +goog.require('ol.xml'); + + +goog.exportSymbol( + 'ol.animation.bounce', + ol.animation.bounce, + OPENLAYERS); + +goog.exportSymbol( + 'ol.animation.pan', + ol.animation.pan, + OPENLAYERS); + +goog.exportSymbol( + 'ol.animation.rotate', + ol.animation.rotate, + OPENLAYERS); + +goog.exportSymbol( + 'ol.animation.zoom', + ol.animation.zoom, + OPENLAYERS); + +goog.exportSymbol( + 'ol.Attribution', + ol.Attribution, + OPENLAYERS); + +goog.exportProperty( + ol.Attribution.prototype, + 'getHTML', + ol.Attribution.prototype.getHTML); + +goog.exportProperty( + ol.CollectionEvent.prototype, + 'element', + ol.CollectionEvent.prototype.element); + +goog.exportSymbol( + 'ol.Collection', + ol.Collection, + OPENLAYERS); + +goog.exportProperty( + ol.Collection.prototype, + 'clear', + ol.Collection.prototype.clear); + +goog.exportProperty( + ol.Collection.prototype, + 'extend', + ol.Collection.prototype.extend); + +goog.exportProperty( + ol.Collection.prototype, + 'forEach', + ol.Collection.prototype.forEach); + +goog.exportProperty( + ol.Collection.prototype, + 'getArray', + ol.Collection.prototype.getArray); + +goog.exportProperty( + ol.Collection.prototype, + 'item', + ol.Collection.prototype.item); + +goog.exportProperty( + ol.Collection.prototype, + 'getLength', + ol.Collection.prototype.getLength); + +goog.exportProperty( + ol.Collection.prototype, + 'insertAt', + ol.Collection.prototype.insertAt); + +goog.exportProperty( + ol.Collection.prototype, + 'pop', + ol.Collection.prototype.pop); + +goog.exportProperty( + ol.Collection.prototype, + 'push', + ol.Collection.prototype.push); + +goog.exportProperty( + ol.Collection.prototype, + 'remove', + ol.Collection.prototype.remove); + +goog.exportProperty( + ol.Collection.prototype, + 'removeAt', + ol.Collection.prototype.removeAt); + +goog.exportProperty( + ol.Collection.prototype, + 'setAt', + ol.Collection.prototype.setAt); + +goog.exportSymbol( + 'ol.colorlike.asColorLike', + ol.colorlike.asColorLike, + OPENLAYERS); + +goog.exportSymbol( + 'ol.coordinate.add', + ol.coordinate.add, + OPENLAYERS); + +goog.exportSymbol( + 'ol.coordinate.createStringXY', + ol.coordinate.createStringXY, + OPENLAYERS); + +goog.exportSymbol( + 'ol.coordinate.format', + ol.coordinate.format, + OPENLAYERS); + +goog.exportSymbol( + 'ol.coordinate.rotate', + ol.coordinate.rotate, + OPENLAYERS); + +goog.exportSymbol( + 'ol.coordinate.toStringHDMS', + ol.coordinate.toStringHDMS, + OPENLAYERS); + +goog.exportSymbol( + 'ol.coordinate.toStringXY', + ol.coordinate.toStringXY, + OPENLAYERS); + +goog.exportSymbol( + 'ol.DeviceOrientation', + ol.DeviceOrientation, + OPENLAYERS); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getAlpha', + ol.DeviceOrientation.prototype.getAlpha); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getBeta', + ol.DeviceOrientation.prototype.getBeta); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getGamma', + ol.DeviceOrientation.prototype.getGamma); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getHeading', + ol.DeviceOrientation.prototype.getHeading); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getTracking', + ol.DeviceOrientation.prototype.getTracking); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'setTracking', + ol.DeviceOrientation.prototype.setTracking); + +goog.exportSymbol( + 'ol.easing.easeIn', + ol.easing.easeIn, + OPENLAYERS); + +goog.exportSymbol( + 'ol.easing.easeOut', + ol.easing.easeOut, + OPENLAYERS); + +goog.exportSymbol( + 'ol.easing.inAndOut', + ol.easing.inAndOut, + OPENLAYERS); + +goog.exportSymbol( + 'ol.easing.linear', + ol.easing.linear, + OPENLAYERS); + +goog.exportSymbol( + 'ol.easing.upAndDown', + ol.easing.upAndDown, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.boundingExtent', + ol.extent.boundingExtent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.buffer', + ol.extent.buffer, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.containsCoordinate', + ol.extent.containsCoordinate, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.containsExtent', + ol.extent.containsExtent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.containsXY', + ol.extent.containsXY, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.createEmpty', + ol.extent.createEmpty, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.equals', + ol.extent.equals, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.extend', + ol.extent.extend, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getBottomLeft', + ol.extent.getBottomLeft, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getBottomRight', + ol.extent.getBottomRight, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getCenter', + ol.extent.getCenter, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getHeight', + ol.extent.getHeight, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getIntersection', + ol.extent.getIntersection, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getSize', + ol.extent.getSize, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getTopLeft', + ol.extent.getTopLeft, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getTopRight', + ol.extent.getTopRight, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.getWidth', + ol.extent.getWidth, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.intersects', + ol.extent.intersects, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.isEmpty', + ol.extent.isEmpty, + OPENLAYERS); + +goog.exportSymbol( + 'ol.extent.applyTransform', + ol.extent.applyTransform, + OPENLAYERS); + +goog.exportSymbol( + 'ol.Feature', + ol.Feature, + OPENLAYERS); + +goog.exportProperty( + ol.Feature.prototype, + 'clone', + ol.Feature.prototype.clone); + +goog.exportProperty( + ol.Feature.prototype, + 'getGeometry', + ol.Feature.prototype.getGeometry); + +goog.exportProperty( + ol.Feature.prototype, + 'getId', + ol.Feature.prototype.getId); + +goog.exportProperty( + ol.Feature.prototype, + 'getGeometryName', + ol.Feature.prototype.getGeometryName); + +goog.exportProperty( + ol.Feature.prototype, + 'getStyle', + ol.Feature.prototype.getStyle); + +goog.exportProperty( + ol.Feature.prototype, + 'getStyleFunction', + ol.Feature.prototype.getStyleFunction); + +goog.exportProperty( + ol.Feature.prototype, + 'setGeometry', + ol.Feature.prototype.setGeometry); + +goog.exportProperty( + ol.Feature.prototype, + 'setStyle', + ol.Feature.prototype.setStyle); + +goog.exportProperty( + ol.Feature.prototype, + 'setId', + ol.Feature.prototype.setId); + +goog.exportProperty( + ol.Feature.prototype, + 'setGeometryName', + ol.Feature.prototype.setGeometryName); + +goog.exportSymbol( + 'ol.featureloader.tile', + ol.featureloader.tile, + OPENLAYERS); + +goog.exportSymbol( + 'ol.featureloader.xhr', + ol.featureloader.xhr, + OPENLAYERS); + +goog.exportSymbol( + 'ol.Geolocation', + ol.Geolocation, + OPENLAYERS); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getAccuracy', + ol.Geolocation.prototype.getAccuracy); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getAccuracyGeometry', + ol.Geolocation.prototype.getAccuracyGeometry); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getAltitude', + ol.Geolocation.prototype.getAltitude); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getAltitudeAccuracy', + ol.Geolocation.prototype.getAltitudeAccuracy); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getHeading', + ol.Geolocation.prototype.getHeading); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getPosition', + ol.Geolocation.prototype.getPosition); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getProjection', + ol.Geolocation.prototype.getProjection); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getSpeed', + ol.Geolocation.prototype.getSpeed); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getTracking', + ol.Geolocation.prototype.getTracking); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getTrackingOptions', + ol.Geolocation.prototype.getTrackingOptions); + +goog.exportProperty( + ol.Geolocation.prototype, + 'setProjection', + ol.Geolocation.prototype.setProjection); + +goog.exportProperty( + ol.Geolocation.prototype, + 'setTracking', + ol.Geolocation.prototype.setTracking); + +goog.exportProperty( + ol.Geolocation.prototype, + 'setTrackingOptions', + ol.Geolocation.prototype.setTrackingOptions); + +goog.exportSymbol( + 'ol.Graticule', + ol.Graticule, + OPENLAYERS); + +goog.exportProperty( + ol.Graticule.prototype, + 'getMap', + ol.Graticule.prototype.getMap); + +goog.exportProperty( + ol.Graticule.prototype, + 'getMeridians', + ol.Graticule.prototype.getMeridians); + +goog.exportProperty( + ol.Graticule.prototype, + 'getParallels', + ol.Graticule.prototype.getParallels); + +goog.exportProperty( + ol.Graticule.prototype, + 'setMap', + ol.Graticule.prototype.setMap); + +goog.exportSymbol( + 'ol.has.DEVICE_PIXEL_RATIO', + ol.has.DEVICE_PIXEL_RATIO, + OPENLAYERS); + +goog.exportSymbol( + 'ol.has.CANVAS', + ol.has.CANVAS, + OPENLAYERS); + +goog.exportSymbol( + 'ol.has.DEVICE_ORIENTATION', + ol.has.DEVICE_ORIENTATION, + OPENLAYERS); + +goog.exportSymbol( + 'ol.has.GEOLOCATION', + ol.has.GEOLOCATION, + OPENLAYERS); + +goog.exportSymbol( + 'ol.has.TOUCH', + ol.has.TOUCH, + OPENLAYERS); + +goog.exportSymbol( + 'ol.has.WEBGL', + ol.has.WEBGL, + OPENLAYERS); + +goog.exportProperty( + ol.Image.prototype, + 'getImage', + ol.Image.prototype.getImage); + +goog.exportProperty( + ol.Image.prototype, + 'load', + ol.Image.prototype.load); + +goog.exportProperty( + ol.ImageTile.prototype, + 'getImage', + ol.ImageTile.prototype.getImage); + +goog.exportProperty( + ol.ImageTile.prototype, + 'load', + ol.ImageTile.prototype.load); + +goog.exportSymbol( + 'ol.Kinetic', + ol.Kinetic, + OPENLAYERS); + +goog.exportSymbol( + 'ol.loadingstrategy.all', + ol.loadingstrategy.all, + OPENLAYERS); + +goog.exportSymbol( + 'ol.loadingstrategy.bbox', + ol.loadingstrategy.bbox, + OPENLAYERS); + +goog.exportSymbol( + 'ol.loadingstrategy.tile', + ol.loadingstrategy.tile, + OPENLAYERS); + +goog.exportSymbol( + 'ol.Map', + ol.Map, + OPENLAYERS); + +goog.exportProperty( + ol.Map.prototype, + 'addControl', + ol.Map.prototype.addControl); + +goog.exportProperty( + ol.Map.prototype, + 'addInteraction', + ol.Map.prototype.addInteraction); + +goog.exportProperty( + ol.Map.prototype, + 'addLayer', + ol.Map.prototype.addLayer); + +goog.exportProperty( + ol.Map.prototype, + 'addOverlay', + ol.Map.prototype.addOverlay); + +goog.exportProperty( + ol.Map.prototype, + 'beforeRender', + ol.Map.prototype.beforeRender); + +goog.exportProperty( + ol.Map.prototype, + 'forEachFeatureAtPixel', + ol.Map.prototype.forEachFeatureAtPixel); + +goog.exportProperty( + ol.Map.prototype, + 'forEachLayerAtPixel', + ol.Map.prototype.forEachLayerAtPixel); + +goog.exportProperty( + ol.Map.prototype, + 'hasFeatureAtPixel', + ol.Map.prototype.hasFeatureAtPixel); + +goog.exportProperty( + ol.Map.prototype, + 'getEventCoordinate', + ol.Map.prototype.getEventCoordinate); + +goog.exportProperty( + ol.Map.prototype, + 'getEventPixel', + ol.Map.prototype.getEventPixel); + +goog.exportProperty( + ol.Map.prototype, + 'getTarget', + ol.Map.prototype.getTarget); + +goog.exportProperty( + ol.Map.prototype, + 'getTargetElement', + ol.Map.prototype.getTargetElement); + +goog.exportProperty( + ol.Map.prototype, + 'getCoordinateFromPixel', + ol.Map.prototype.getCoordinateFromPixel); + +goog.exportProperty( + ol.Map.prototype, + 'getControls', + ol.Map.prototype.getControls); + +goog.exportProperty( + ol.Map.prototype, + 'getOverlays', + ol.Map.prototype.getOverlays); + +goog.exportProperty( + ol.Map.prototype, + 'getOverlayById', + ol.Map.prototype.getOverlayById); + +goog.exportProperty( + ol.Map.prototype, + 'getInteractions', + ol.Map.prototype.getInteractions); + +goog.exportProperty( + ol.Map.prototype, + 'getLayerGroup', + ol.Map.prototype.getLayerGroup); + +goog.exportProperty( + ol.Map.prototype, + 'getLayers', + ol.Map.prototype.getLayers); + +goog.exportProperty( + ol.Map.prototype, + 'getPixelFromCoordinate', + ol.Map.prototype.getPixelFromCoordinate); + +goog.exportProperty( + ol.Map.prototype, + 'getSize', + ol.Map.prototype.getSize); + +goog.exportProperty( + ol.Map.prototype, + 'getView', + ol.Map.prototype.getView); + +goog.exportProperty( + ol.Map.prototype, + 'getViewport', + ol.Map.prototype.getViewport); + +goog.exportProperty( + ol.Map.prototype, + 'renderSync', + ol.Map.prototype.renderSync); + +goog.exportProperty( + ol.Map.prototype, + 'render', + ol.Map.prototype.render); + +goog.exportProperty( + ol.Map.prototype, + 'removeControl', + ol.Map.prototype.removeControl); + +goog.exportProperty( + ol.Map.prototype, + 'removeInteraction', + ol.Map.prototype.removeInteraction); + +goog.exportProperty( + ol.Map.prototype, + 'removeLayer', + ol.Map.prototype.removeLayer); + +goog.exportProperty( + ol.Map.prototype, + 'removeOverlay', + ol.Map.prototype.removeOverlay); + +goog.exportProperty( + ol.Map.prototype, + 'setLayerGroup', + ol.Map.prototype.setLayerGroup); + +goog.exportProperty( + ol.Map.prototype, + 'setSize', + ol.Map.prototype.setSize); + +goog.exportProperty( + ol.Map.prototype, + 'setTarget', + ol.Map.prototype.setTarget); + +goog.exportProperty( + ol.Map.prototype, + 'setView', + ol.Map.prototype.setView); + +goog.exportProperty( + ol.Map.prototype, + 'updateSize', + ol.Map.prototype.updateSize); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'originalEvent', + ol.MapBrowserEvent.prototype.originalEvent); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'pixel', + ol.MapBrowserEvent.prototype.pixel); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'coordinate', + ol.MapBrowserEvent.prototype.coordinate); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'dragging', + ol.MapBrowserEvent.prototype.dragging); + +goog.exportProperty( + ol.MapEvent.prototype, + 'map', + ol.MapEvent.prototype.map); + +goog.exportProperty( + ol.MapEvent.prototype, + 'frameState', + ol.MapEvent.prototype.frameState); + +goog.exportProperty( + ol.ObjectEvent.prototype, + 'key', + ol.ObjectEvent.prototype.key); + +goog.exportProperty( + ol.ObjectEvent.prototype, + 'oldValue', + ol.ObjectEvent.prototype.oldValue); + +goog.exportSymbol( + 'ol.Object', + ol.Object, + OPENLAYERS); + +goog.exportProperty( + ol.Object.prototype, + 'get', + ol.Object.prototype.get); + +goog.exportProperty( + ol.Object.prototype, + 'getKeys', + ol.Object.prototype.getKeys); + +goog.exportProperty( + ol.Object.prototype, + 'getProperties', + ol.Object.prototype.getProperties); + +goog.exportProperty( + ol.Object.prototype, + 'set', + ol.Object.prototype.set); + +goog.exportProperty( + ol.Object.prototype, + 'setProperties', + ol.Object.prototype.setProperties); + +goog.exportProperty( + ol.Object.prototype, + 'unset', + ol.Object.prototype.unset); + +goog.exportSymbol( + 'ol.Observable', + ol.Observable, + OPENLAYERS); + +goog.exportSymbol( + 'ol.Observable.unByKey', + ol.Observable.unByKey, + OPENLAYERS); + +goog.exportProperty( + ol.Observable.prototype, + 'changed', + ol.Observable.prototype.changed); + +goog.exportProperty( + ol.Observable.prototype, + 'dispatchEvent', + ol.Observable.prototype.dispatchEvent); + +goog.exportProperty( + ol.Observable.prototype, + 'getRevision', + ol.Observable.prototype.getRevision); + +goog.exportProperty( + ol.Observable.prototype, + 'on', + ol.Observable.prototype.on); + +goog.exportProperty( + ol.Observable.prototype, + 'once', + ol.Observable.prototype.once); + +goog.exportProperty( + ol.Observable.prototype, + 'un', + ol.Observable.prototype.un); + +goog.exportProperty( + ol.Observable.prototype, + 'unByKey', + ol.Observable.prototype.unByKey); + +goog.exportSymbol( + 'ol.inherits', + ol.inherits, + OPENLAYERS); + +goog.exportSymbol( + 'ol.Overlay', + ol.Overlay, + OPENLAYERS); + +goog.exportProperty( + ol.Overlay.prototype, + 'getElement', + ol.Overlay.prototype.getElement); + +goog.exportProperty( + ol.Overlay.prototype, + 'getId', + ol.Overlay.prototype.getId); + +goog.exportProperty( + ol.Overlay.prototype, + 'getMap', + ol.Overlay.prototype.getMap); + +goog.exportProperty( + ol.Overlay.prototype, + 'getOffset', + ol.Overlay.prototype.getOffset); + +goog.exportProperty( + ol.Overlay.prototype, + 'getPosition', + ol.Overlay.prototype.getPosition); + +goog.exportProperty( + ol.Overlay.prototype, + 'getPositioning', + ol.Overlay.prototype.getPositioning); + +goog.exportProperty( + ol.Overlay.prototype, + 'setElement', + ol.Overlay.prototype.setElement); + +goog.exportProperty( + ol.Overlay.prototype, + 'setMap', + ol.Overlay.prototype.setMap); + +goog.exportProperty( + ol.Overlay.prototype, + 'setOffset', + ol.Overlay.prototype.setOffset); + +goog.exportProperty( + ol.Overlay.prototype, + 'setPosition', + ol.Overlay.prototype.setPosition); + +goog.exportProperty( + ol.Overlay.prototype, + 'setPositioning', + ol.Overlay.prototype.setPositioning); + +goog.exportSymbol( + 'ol.render.toContext', + ol.render.toContext, + OPENLAYERS); + +goog.exportSymbol( + 'ol.size.toSize', + ol.size.toSize, + OPENLAYERS); + +goog.exportProperty( + ol.Tile.prototype, + 'getTileCoord', + ol.Tile.prototype.getTileCoord); + +goog.exportProperty( + ol.Tile.prototype, + 'load', + ol.Tile.prototype.load); + +goog.exportProperty( + ol.VectorTile.prototype, + 'getFormat', + ol.VectorTile.prototype.getFormat); + +goog.exportProperty( + ol.VectorTile.prototype, + 'setFeatures', + ol.VectorTile.prototype.setFeatures); + +goog.exportProperty( + ol.VectorTile.prototype, + 'setProjection', + ol.VectorTile.prototype.setProjection); + +goog.exportProperty( + ol.VectorTile.prototype, + 'setLoader', + ol.VectorTile.prototype.setLoader); + +goog.exportSymbol( + 'ol.View', + ol.View, + OPENLAYERS); + +goog.exportProperty( + ol.View.prototype, + 'constrainCenter', + ol.View.prototype.constrainCenter); + +goog.exportProperty( + ol.View.prototype, + 'constrainResolution', + ol.View.prototype.constrainResolution); + +goog.exportProperty( + ol.View.prototype, + 'constrainRotation', + ol.View.prototype.constrainRotation); + +goog.exportProperty( + ol.View.prototype, + 'getCenter', + ol.View.prototype.getCenter); + +goog.exportProperty( + ol.View.prototype, + 'calculateExtent', + ol.View.prototype.calculateExtent); + +goog.exportProperty( + ol.View.prototype, + 'getMaxResolution', + ol.View.prototype.getMaxResolution); + +goog.exportProperty( + ol.View.prototype, + 'getMinResolution', + ol.View.prototype.getMinResolution); + +goog.exportProperty( + ol.View.prototype, + 'getProjection', + ol.View.prototype.getProjection); + +goog.exportProperty( + ol.View.prototype, + 'getResolution', + ol.View.prototype.getResolution); + +goog.exportProperty( + ol.View.prototype, + 'getResolutions', + ol.View.prototype.getResolutions); + +goog.exportProperty( + ol.View.prototype, + 'getRotation', + ol.View.prototype.getRotation); + +goog.exportProperty( + ol.View.prototype, + 'getZoom', + ol.View.prototype.getZoom); + +goog.exportProperty( + ol.View.prototype, + 'fit', + ol.View.prototype.fit); + +goog.exportProperty( + ol.View.prototype, + 'centerOn', + ol.View.prototype.centerOn); + +goog.exportProperty( + ol.View.prototype, + 'rotate', + ol.View.prototype.rotate); + +goog.exportProperty( + ol.View.prototype, + 'setCenter', + ol.View.prototype.setCenter); + +goog.exportProperty( + ol.View.prototype, + 'setResolution', + ol.View.prototype.setResolution); + +goog.exportProperty( + ol.View.prototype, + 'setRotation', + ol.View.prototype.setRotation); + +goog.exportProperty( + ol.View.prototype, + 'setZoom', + ol.View.prototype.setZoom); + +goog.exportSymbol( + 'ol.xml.getAllTextContent', + ol.xml.getAllTextContent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.xml.parse', + ol.xml.parse, + OPENLAYERS); + +goog.exportProperty( + ol.webgl.Context.prototype, + 'getGL', + ol.webgl.Context.prototype.getGL); + +goog.exportProperty( + ol.webgl.Context.prototype, + 'useProgram', + ol.webgl.Context.prototype.useProgram); + +goog.exportSymbol( + 'ol.tilegrid.TileGrid', + ol.tilegrid.TileGrid, + OPENLAYERS); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'forEachTileCoord', + ol.tilegrid.TileGrid.prototype.forEachTileCoord); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getMaxZoom', + ol.tilegrid.TileGrid.prototype.getMaxZoom); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getMinZoom', + ol.tilegrid.TileGrid.prototype.getMinZoom); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getOrigin', + ol.tilegrid.TileGrid.prototype.getOrigin); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getResolution', + ol.tilegrid.TileGrid.prototype.getResolution); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getResolutions', + ol.tilegrid.TileGrid.prototype.getResolutions); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getTileCoordExtent', + ol.tilegrid.TileGrid.prototype.getTileCoordExtent); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getTileCoordForCoordAndResolution', + ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndResolution); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getTileCoordForCoordAndZ', + ol.tilegrid.TileGrid.prototype.getTileCoordForCoordAndZ); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getTileSize', + ol.tilegrid.TileGrid.prototype.getTileSize); + +goog.exportProperty( + ol.tilegrid.TileGrid.prototype, + 'getZForResolution', + ol.tilegrid.TileGrid.prototype.getZForResolution); + +goog.exportSymbol( + 'ol.tilegrid.createXYZ', + ol.tilegrid.createXYZ, + OPENLAYERS); + +goog.exportSymbol( + 'ol.tilegrid.WMTS', + ol.tilegrid.WMTS, + OPENLAYERS); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getMatrixIds', + ol.tilegrid.WMTS.prototype.getMatrixIds); + +goog.exportSymbol( + 'ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet', + ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet, + OPENLAYERS); + +goog.exportSymbol( + 'ol.style.AtlasManager', + ol.style.AtlasManager, + OPENLAYERS); + +goog.exportSymbol( + 'ol.style.Circle', + ol.style.Circle, + OPENLAYERS); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getFill', + ol.style.Circle.prototype.getFill); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getImage', + ol.style.Circle.prototype.getImage); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getRadius', + ol.style.Circle.prototype.getRadius); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getStroke', + ol.style.Circle.prototype.getStroke); + +goog.exportSymbol( + 'ol.style.Fill', + ol.style.Fill, + OPENLAYERS); + +goog.exportProperty( + ol.style.Fill.prototype, + 'getColor', + ol.style.Fill.prototype.getColor); + +goog.exportProperty( + ol.style.Fill.prototype, + 'setColor', + ol.style.Fill.prototype.setColor); + +goog.exportSymbol( + 'ol.style.Icon', + ol.style.Icon, + OPENLAYERS); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getAnchor', + ol.style.Icon.prototype.getAnchor); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getImage', + ol.style.Icon.prototype.getImage); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getOrigin', + ol.style.Icon.prototype.getOrigin); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getSrc', + ol.style.Icon.prototype.getSrc); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getSize', + ol.style.Icon.prototype.getSize); + +goog.exportProperty( + ol.style.Icon.prototype, + 'load', + ol.style.Icon.prototype.load); + +goog.exportSymbol( + 'ol.style.Image', + ol.style.Image, + OPENLAYERS); + +goog.exportProperty( + ol.style.Image.prototype, + 'getOpacity', + ol.style.Image.prototype.getOpacity); + +goog.exportProperty( + ol.style.Image.prototype, + 'getRotateWithView', + ol.style.Image.prototype.getRotateWithView); + +goog.exportProperty( + ol.style.Image.prototype, + 'getRotation', + ol.style.Image.prototype.getRotation); + +goog.exportProperty( + ol.style.Image.prototype, + 'getScale', + ol.style.Image.prototype.getScale); + +goog.exportProperty( + ol.style.Image.prototype, + 'getSnapToPixel', + ol.style.Image.prototype.getSnapToPixel); + +goog.exportProperty( + ol.style.Image.prototype, + 'setOpacity', + ol.style.Image.prototype.setOpacity); + +goog.exportProperty( + ol.style.Image.prototype, + 'setRotation', + ol.style.Image.prototype.setRotation); + +goog.exportProperty( + ol.style.Image.prototype, + 'setScale', + ol.style.Image.prototype.setScale); + +goog.exportSymbol( + 'ol.style.RegularShape', + ol.style.RegularShape, + OPENLAYERS); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getAnchor', + ol.style.RegularShape.prototype.getAnchor); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getAngle', + ol.style.RegularShape.prototype.getAngle); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getFill', + ol.style.RegularShape.prototype.getFill); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getImage', + ol.style.RegularShape.prototype.getImage); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getOrigin', + ol.style.RegularShape.prototype.getOrigin); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getPoints', + ol.style.RegularShape.prototype.getPoints); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getRadius', + ol.style.RegularShape.prototype.getRadius); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getRadius2', + ol.style.RegularShape.prototype.getRadius2); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getSize', + ol.style.RegularShape.prototype.getSize); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getStroke', + ol.style.RegularShape.prototype.getStroke); + +goog.exportSymbol( + 'ol.style.Stroke', + ol.style.Stroke, + OPENLAYERS); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'getColor', + ol.style.Stroke.prototype.getColor); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'getLineCap', + ol.style.Stroke.prototype.getLineCap); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'getLineDash', + ol.style.Stroke.prototype.getLineDash); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'getLineJoin', + ol.style.Stroke.prototype.getLineJoin); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'getMiterLimit', + ol.style.Stroke.prototype.getMiterLimit); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'getWidth', + ol.style.Stroke.prototype.getWidth); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'setColor', + ol.style.Stroke.prototype.setColor); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'setLineCap', + ol.style.Stroke.prototype.setLineCap); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'setLineDash', + ol.style.Stroke.prototype.setLineDash); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'setLineJoin', + ol.style.Stroke.prototype.setLineJoin); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'setMiterLimit', + ol.style.Stroke.prototype.setMiterLimit); + +goog.exportProperty( + ol.style.Stroke.prototype, + 'setWidth', + ol.style.Stroke.prototype.setWidth); + +goog.exportSymbol( + 'ol.style.Style', + ol.style.Style, + OPENLAYERS); + +goog.exportProperty( + ol.style.Style.prototype, + 'getGeometry', + ol.style.Style.prototype.getGeometry); + +goog.exportProperty( + ol.style.Style.prototype, + 'getGeometryFunction', + ol.style.Style.prototype.getGeometryFunction); + +goog.exportProperty( + ol.style.Style.prototype, + 'getFill', + ol.style.Style.prototype.getFill); + +goog.exportProperty( + ol.style.Style.prototype, + 'getImage', + ol.style.Style.prototype.getImage); + +goog.exportProperty( + ol.style.Style.prototype, + 'getStroke', + ol.style.Style.prototype.getStroke); + +goog.exportProperty( + ol.style.Style.prototype, + 'getText', + ol.style.Style.prototype.getText); + +goog.exportProperty( + ol.style.Style.prototype, + 'getZIndex', + ol.style.Style.prototype.getZIndex); + +goog.exportProperty( + ol.style.Style.prototype, + 'setGeometry', + ol.style.Style.prototype.setGeometry); + +goog.exportProperty( + ol.style.Style.prototype, + 'setZIndex', + ol.style.Style.prototype.setZIndex); + +goog.exportSymbol( + 'ol.style.Text', + ol.style.Text, + OPENLAYERS); + +goog.exportProperty( + ol.style.Text.prototype, + 'getFont', + ol.style.Text.prototype.getFont); + +goog.exportProperty( + ol.style.Text.prototype, + 'getOffsetX', + ol.style.Text.prototype.getOffsetX); + +goog.exportProperty( + ol.style.Text.prototype, + 'getOffsetY', + ol.style.Text.prototype.getOffsetY); + +goog.exportProperty( + ol.style.Text.prototype, + 'getFill', + ol.style.Text.prototype.getFill); + +goog.exportProperty( + ol.style.Text.prototype, + 'getRotation', + ol.style.Text.prototype.getRotation); + +goog.exportProperty( + ol.style.Text.prototype, + 'getScale', + ol.style.Text.prototype.getScale); + +goog.exportProperty( + ol.style.Text.prototype, + 'getStroke', + ol.style.Text.prototype.getStroke); + +goog.exportProperty( + ol.style.Text.prototype, + 'getText', + ol.style.Text.prototype.getText); + +goog.exportProperty( + ol.style.Text.prototype, + 'getTextAlign', + ol.style.Text.prototype.getTextAlign); + +goog.exportProperty( + ol.style.Text.prototype, + 'getTextBaseline', + ol.style.Text.prototype.getTextBaseline); + +goog.exportProperty( + ol.style.Text.prototype, + 'setFont', + ol.style.Text.prototype.setFont); + +goog.exportProperty( + ol.style.Text.prototype, + 'setOffsetX', + ol.style.Text.prototype.setOffsetX); + +goog.exportProperty( + ol.style.Text.prototype, + 'setOffsetY', + ol.style.Text.prototype.setOffsetY); + +goog.exportProperty( + ol.style.Text.prototype, + 'setFill', + ol.style.Text.prototype.setFill); + +goog.exportProperty( + ol.style.Text.prototype, + 'setRotation', + ol.style.Text.prototype.setRotation); + +goog.exportProperty( + ol.style.Text.prototype, + 'setScale', + ol.style.Text.prototype.setScale); + +goog.exportProperty( + ol.style.Text.prototype, + 'setStroke', + ol.style.Text.prototype.setStroke); + +goog.exportProperty( + ol.style.Text.prototype, + 'setText', + ol.style.Text.prototype.setText); + +goog.exportProperty( + ol.style.Text.prototype, + 'setTextAlign', + ol.style.Text.prototype.setTextAlign); + +goog.exportProperty( + ol.style.Text.prototype, + 'setTextBaseline', + ol.style.Text.prototype.setTextBaseline); + +goog.exportSymbol( + 'ol.Sphere', + ol.Sphere, + OPENLAYERS); + +goog.exportProperty( + ol.Sphere.prototype, + 'geodesicArea', + ol.Sphere.prototype.geodesicArea); + +goog.exportProperty( + ol.Sphere.prototype, + 'haversineDistance', + ol.Sphere.prototype.haversineDistance); + +goog.exportSymbol( + 'ol.source.BingMaps', + ol.source.BingMaps, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.BingMaps.TOS_ATTRIBUTION', + ol.source.BingMaps.TOS_ATTRIBUTION, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.CartoDB', + ol.source.CartoDB, + OPENLAYERS); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getConfig', + ol.source.CartoDB.prototype.getConfig); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'updateConfig', + ol.source.CartoDB.prototype.updateConfig); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setConfig', + ol.source.CartoDB.prototype.setConfig); + +goog.exportSymbol( + 'ol.source.Cluster', + ol.source.Cluster, + OPENLAYERS); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getSource', + ol.source.Cluster.prototype.getSource); + +goog.exportSymbol( + 'ol.source.ImageArcGISRest', + ol.source.ImageArcGISRest, + OPENLAYERS); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getParams', + ol.source.ImageArcGISRest.prototype.getParams); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getImageLoadFunction', + ol.source.ImageArcGISRest.prototype.getImageLoadFunction); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getUrl', + ol.source.ImageArcGISRest.prototype.getUrl); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'setImageLoadFunction', + ol.source.ImageArcGISRest.prototype.setImageLoadFunction); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'setUrl', + ol.source.ImageArcGISRest.prototype.setUrl); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'updateParams', + ol.source.ImageArcGISRest.prototype.updateParams); + +goog.exportSymbol( + 'ol.source.ImageCanvas', + ol.source.ImageCanvas, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.ImageMapGuide', + ol.source.ImageMapGuide, + OPENLAYERS); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getParams', + ol.source.ImageMapGuide.prototype.getParams); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getImageLoadFunction', + ol.source.ImageMapGuide.prototype.getImageLoadFunction); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'updateParams', + ol.source.ImageMapGuide.prototype.updateParams); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'setImageLoadFunction', + ol.source.ImageMapGuide.prototype.setImageLoadFunction); + +goog.exportSymbol( + 'ol.source.Image', + ol.source.Image, + OPENLAYERS); + +goog.exportProperty( + ol.source.ImageEvent.prototype, + 'image', + ol.source.ImageEvent.prototype.image); + +goog.exportSymbol( + 'ol.source.ImageStatic', + ol.source.ImageStatic, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.ImageVector', + ol.source.ImageVector, + OPENLAYERS); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getSource', + ol.source.ImageVector.prototype.getSource); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getStyle', + ol.source.ImageVector.prototype.getStyle); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getStyleFunction', + ol.source.ImageVector.prototype.getStyleFunction); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'setStyle', + ol.source.ImageVector.prototype.setStyle); + +goog.exportSymbol( + 'ol.source.ImageWMS', + ol.source.ImageWMS, + OPENLAYERS); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getGetFeatureInfoUrl', + ol.source.ImageWMS.prototype.getGetFeatureInfoUrl); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getParams', + ol.source.ImageWMS.prototype.getParams); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getImageLoadFunction', + ol.source.ImageWMS.prototype.getImageLoadFunction); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getUrl', + ol.source.ImageWMS.prototype.getUrl); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'setImageLoadFunction', + ol.source.ImageWMS.prototype.setImageLoadFunction); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'setUrl', + ol.source.ImageWMS.prototype.setUrl); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'updateParams', + ol.source.ImageWMS.prototype.updateParams); + +goog.exportSymbol( + 'ol.source.OSM', + ol.source.OSM, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.OSM.ATTRIBUTION', + ol.source.OSM.ATTRIBUTION, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.Raster', + ol.source.Raster, + OPENLAYERS); + +goog.exportProperty( + ol.source.Raster.prototype, + 'setOperation', + ol.source.Raster.prototype.setOperation); + +goog.exportProperty( + ol.source.RasterEvent.prototype, + 'extent', + ol.source.RasterEvent.prototype.extent); + +goog.exportProperty( + ol.source.RasterEvent.prototype, + 'resolution', + ol.source.RasterEvent.prototype.resolution); + +goog.exportProperty( + ol.source.RasterEvent.prototype, + 'data', + ol.source.RasterEvent.prototype.data); + +goog.exportSymbol( + 'ol.source.Source', + ol.source.Source, + OPENLAYERS); + +goog.exportProperty( + ol.source.Source.prototype, + 'getAttributions', + ol.source.Source.prototype.getAttributions); + +goog.exportProperty( + ol.source.Source.prototype, + 'getLogo', + ol.source.Source.prototype.getLogo); + +goog.exportProperty( + ol.source.Source.prototype, + 'getProjection', + ol.source.Source.prototype.getProjection); + +goog.exportProperty( + ol.source.Source.prototype, + 'getState', + ol.source.Source.prototype.getState); + +goog.exportProperty( + ol.source.Source.prototype, + 'refresh', + ol.source.Source.prototype.refresh); + +goog.exportProperty( + ol.source.Source.prototype, + 'setAttributions', + ol.source.Source.prototype.setAttributions); + +goog.exportSymbol( + 'ol.source.Stamen', + ol.source.Stamen, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.TileArcGISRest', + ol.source.TileArcGISRest, + OPENLAYERS); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getParams', + ol.source.TileArcGISRest.prototype.getParams); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'updateParams', + ol.source.TileArcGISRest.prototype.updateParams); + +goog.exportSymbol( + 'ol.source.TileDebug', + ol.source.TileDebug, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.TileImage', + ol.source.TileImage, + OPENLAYERS); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setRenderReprojectionEdges', + ol.source.TileImage.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setTileGridForProjection', + ol.source.TileImage.prototype.setTileGridForProjection); + +goog.exportSymbol( + 'ol.source.TileJSON', + ol.source.TileJSON, + OPENLAYERS); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getTileJSON', + ol.source.TileJSON.prototype.getTileJSON); + +goog.exportSymbol( + 'ol.source.Tile', + ol.source.Tile, + OPENLAYERS); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getTileGrid', + ol.source.Tile.prototype.getTileGrid); + +goog.exportProperty( + ol.source.TileEvent.prototype, + 'tile', + ol.source.TileEvent.prototype.tile); + +goog.exportSymbol( + 'ol.source.TileUTFGrid', + ol.source.TileUTFGrid, + OPENLAYERS); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getTemplate', + ol.source.TileUTFGrid.prototype.getTemplate); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'forDataAtCoordinateAndResolution', + ol.source.TileUTFGrid.prototype.forDataAtCoordinateAndResolution); + +goog.exportSymbol( + 'ol.source.TileWMS', + ol.source.TileWMS, + OPENLAYERS); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getGetFeatureInfoUrl', + ol.source.TileWMS.prototype.getGetFeatureInfoUrl); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getParams', + ol.source.TileWMS.prototype.getParams); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'updateParams', + ol.source.TileWMS.prototype.updateParams); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getTileLoadFunction', + ol.source.UrlTile.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getTileUrlFunction', + ol.source.UrlTile.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getUrls', + ol.source.UrlTile.prototype.getUrls); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'setTileLoadFunction', + ol.source.UrlTile.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'setTileUrlFunction', + ol.source.UrlTile.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'setUrl', + ol.source.UrlTile.prototype.setUrl); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'setUrls', + ol.source.UrlTile.prototype.setUrls); + +goog.exportSymbol( + 'ol.source.Vector', + ol.source.Vector, + OPENLAYERS); + +goog.exportProperty( + ol.source.Vector.prototype, + 'addFeature', + ol.source.Vector.prototype.addFeature); + +goog.exportProperty( + ol.source.Vector.prototype, + 'addFeatures', + ol.source.Vector.prototype.addFeatures); + +goog.exportProperty( + ol.source.Vector.prototype, + 'clear', + ol.source.Vector.prototype.clear); + +goog.exportProperty( + ol.source.Vector.prototype, + 'forEachFeature', + ol.source.Vector.prototype.forEachFeature); + +goog.exportProperty( + ol.source.Vector.prototype, + 'forEachFeatureInExtent', + ol.source.Vector.prototype.forEachFeatureInExtent); + +goog.exportProperty( + ol.source.Vector.prototype, + 'forEachFeatureIntersectingExtent', + ol.source.Vector.prototype.forEachFeatureIntersectingExtent); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getFeaturesCollection', + ol.source.Vector.prototype.getFeaturesCollection); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getFeatures', + ol.source.Vector.prototype.getFeatures); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getFeaturesAtCoordinate', + ol.source.Vector.prototype.getFeaturesAtCoordinate); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getFeaturesInExtent', + ol.source.Vector.prototype.getFeaturesInExtent); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getClosestFeatureToCoordinate', + ol.source.Vector.prototype.getClosestFeatureToCoordinate); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getExtent', + ol.source.Vector.prototype.getExtent); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getFeatureById', + ol.source.Vector.prototype.getFeatureById); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getFormat', + ol.source.Vector.prototype.getFormat); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getUrl', + ol.source.Vector.prototype.getUrl); + +goog.exportProperty( + ol.source.Vector.prototype, + 'removeFeature', + ol.source.Vector.prototype.removeFeature); + +goog.exportProperty( + ol.source.VectorEvent.prototype, + 'feature', + ol.source.VectorEvent.prototype.feature); + +goog.exportSymbol( + 'ol.source.VectorTile', + ol.source.VectorTile, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.WMTS', + ol.source.WMTS, + OPENLAYERS); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getDimensions', + ol.source.WMTS.prototype.getDimensions); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getFormat', + ol.source.WMTS.prototype.getFormat); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getLayer', + ol.source.WMTS.prototype.getLayer); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getMatrixSet', + ol.source.WMTS.prototype.getMatrixSet); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getRequestEncoding', + ol.source.WMTS.prototype.getRequestEncoding); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getStyle', + ol.source.WMTS.prototype.getStyle); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getVersion', + ol.source.WMTS.prototype.getVersion); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'updateDimensions', + ol.source.WMTS.prototype.updateDimensions); + +goog.exportSymbol( + 'ol.source.WMTS.optionsFromCapabilities', + ol.source.WMTS.optionsFromCapabilities, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.XYZ', + ol.source.XYZ, + OPENLAYERS); + +goog.exportSymbol( + 'ol.source.Zoomify', + ol.source.Zoomify, + OPENLAYERS); + +goog.exportProperty( + ol.render.Event.prototype, + 'vectorContext', + ol.render.Event.prototype.vectorContext); + +goog.exportProperty( + ol.render.Event.prototype, + 'frameState', + ol.render.Event.prototype.frameState); + +goog.exportProperty( + ol.render.Event.prototype, + 'context', + ol.render.Event.prototype.context); + +goog.exportProperty( + ol.render.Event.prototype, + 'glContext', + ol.render.Event.prototype.glContext); + +goog.exportProperty( + ol.render.Feature.prototype, + 'get', + ol.render.Feature.prototype.get); + +goog.exportProperty( + ol.render.Feature.prototype, + 'getExtent', + ol.render.Feature.prototype.getExtent); + +goog.exportProperty( + ol.render.Feature.prototype, + 'getGeometry', + ol.render.Feature.prototype.getGeometry); + +goog.exportProperty( + ol.render.Feature.prototype, + 'getProperties', + ol.render.Feature.prototype.getProperties); + +goog.exportProperty( + ol.render.Feature.prototype, + 'getType', + ol.render.Feature.prototype.getType); + +goog.exportSymbol( + 'ol.render.VectorContext', + ol.render.VectorContext, + OPENLAYERS); + +goog.exportProperty( + ol.render.webgl.Immediate.prototype, + 'setStyle', + ol.render.webgl.Immediate.prototype.setStyle); + +goog.exportProperty( + ol.render.webgl.Immediate.prototype, + 'drawGeometry', + ol.render.webgl.Immediate.prototype.drawGeometry); + +goog.exportProperty( + ol.render.webgl.Immediate.prototype, + 'drawFeature', + ol.render.webgl.Immediate.prototype.drawFeature); + +goog.exportProperty( + ol.render.canvas.Immediate.prototype, + 'drawCircle', + ol.render.canvas.Immediate.prototype.drawCircle); + +goog.exportProperty( + ol.render.canvas.Immediate.prototype, + 'setStyle', + ol.render.canvas.Immediate.prototype.setStyle); + +goog.exportProperty( + ol.render.canvas.Immediate.prototype, + 'drawGeometry', + ol.render.canvas.Immediate.prototype.drawGeometry); + +goog.exportProperty( + ol.render.canvas.Immediate.prototype, + 'drawFeature', + ol.render.canvas.Immediate.prototype.drawFeature); + +goog.exportSymbol( + 'ol.proj.common.add', + ol.proj.common.add, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.METERS_PER_UNIT', + ol.proj.METERS_PER_UNIT, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.Projection', + ol.proj.Projection, + OPENLAYERS); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'getCode', + ol.proj.Projection.prototype.getCode); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'getExtent', + ol.proj.Projection.prototype.getExtent); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'getUnits', + ol.proj.Projection.prototype.getUnits); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'getMetersPerUnit', + ol.proj.Projection.prototype.getMetersPerUnit); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'getWorldExtent', + ol.proj.Projection.prototype.getWorldExtent); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'isGlobal', + ol.proj.Projection.prototype.isGlobal); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'setGlobal', + ol.proj.Projection.prototype.setGlobal); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'setExtent', + ol.proj.Projection.prototype.setExtent); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'setWorldExtent', + ol.proj.Projection.prototype.setWorldExtent); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'setGetPointResolution', + ol.proj.Projection.prototype.setGetPointResolution); + +goog.exportProperty( + ol.proj.Projection.prototype, + 'getPointResolution', + ol.proj.Projection.prototype.getPointResolution); + +goog.exportSymbol( + 'ol.proj.setProj4', + ol.proj.setProj4, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.addEquivalentProjections', + ol.proj.addEquivalentProjections, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.addProjection', + ol.proj.addProjection, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.addCoordinateTransforms', + ol.proj.addCoordinateTransforms, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.fromLonLat', + ol.proj.fromLonLat, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.toLonLat', + ol.proj.toLonLat, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.get', + ol.proj.get, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.equivalent', + ol.proj.equivalent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.getTransform', + ol.proj.getTransform, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.transform', + ol.proj.transform, + OPENLAYERS); + +goog.exportSymbol( + 'ol.proj.transformExtent', + ol.proj.transformExtent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.layer.Heatmap', + ol.layer.Heatmap, + OPENLAYERS); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getBlur', + ol.layer.Heatmap.prototype.getBlur); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getGradient', + ol.layer.Heatmap.prototype.getGradient); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getRadius', + ol.layer.Heatmap.prototype.getRadius); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setBlur', + ol.layer.Heatmap.prototype.setBlur); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setGradient', + ol.layer.Heatmap.prototype.setGradient); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setRadius', + ol.layer.Heatmap.prototype.setRadius); + +goog.exportSymbol( + 'ol.layer.Image', + ol.layer.Image, + OPENLAYERS); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getSource', + ol.layer.Image.prototype.getSource); + +goog.exportSymbol( + 'ol.layer.Layer', + ol.layer.Layer, + OPENLAYERS); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getSource', + ol.layer.Layer.prototype.getSource); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setMap', + ol.layer.Layer.prototype.setMap); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setSource', + ol.layer.Layer.prototype.setSource); + +goog.exportSymbol( + 'ol.layer.Base', + ol.layer.Base, + OPENLAYERS); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getExtent', + ol.layer.Base.prototype.getExtent); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getMaxResolution', + ol.layer.Base.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getMinResolution', + ol.layer.Base.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getOpacity', + ol.layer.Base.prototype.getOpacity); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getVisible', + ol.layer.Base.prototype.getVisible); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getZIndex', + ol.layer.Base.prototype.getZIndex); + +goog.exportProperty( + ol.layer.Base.prototype, + 'setExtent', + ol.layer.Base.prototype.setExtent); + +goog.exportProperty( + ol.layer.Base.prototype, + 'setMaxResolution', + ol.layer.Base.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.Base.prototype, + 'setMinResolution', + ol.layer.Base.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.Base.prototype, + 'setOpacity', + ol.layer.Base.prototype.setOpacity); + +goog.exportProperty( + ol.layer.Base.prototype, + 'setVisible', + ol.layer.Base.prototype.setVisible); + +goog.exportProperty( + ol.layer.Base.prototype, + 'setZIndex', + ol.layer.Base.prototype.setZIndex); + +goog.exportSymbol( + 'ol.layer.Group', + ol.layer.Group, + OPENLAYERS); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getLayers', + ol.layer.Group.prototype.getLayers); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setLayers', + ol.layer.Group.prototype.setLayers); + +goog.exportSymbol( + 'ol.layer.Tile', + ol.layer.Tile, + OPENLAYERS); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getPreload', + ol.layer.Tile.prototype.getPreload); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getSource', + ol.layer.Tile.prototype.getSource); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setPreload', + ol.layer.Tile.prototype.setPreload); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getUseInterimTilesOnError', + ol.layer.Tile.prototype.getUseInterimTilesOnError); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setUseInterimTilesOnError', + ol.layer.Tile.prototype.setUseInterimTilesOnError); + +goog.exportSymbol( + 'ol.layer.Vector', + ol.layer.Vector, + OPENLAYERS); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getSource', + ol.layer.Vector.prototype.getSource); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getStyle', + ol.layer.Vector.prototype.getStyle); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getStyleFunction', + ol.layer.Vector.prototype.getStyleFunction); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setStyle', + ol.layer.Vector.prototype.setStyle); + +goog.exportSymbol( + 'ol.layer.VectorTile', + ol.layer.VectorTile, + OPENLAYERS); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getPreload', + ol.layer.VectorTile.prototype.getPreload); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getUseInterimTilesOnError', + ol.layer.VectorTile.prototype.getUseInterimTilesOnError); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setPreload', + ol.layer.VectorTile.prototype.setPreload); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setUseInterimTilesOnError', + ol.layer.VectorTile.prototype.setUseInterimTilesOnError); + +goog.exportSymbol( + 'ol.interaction.DoubleClickZoom', + ol.interaction.DoubleClickZoom, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.DoubleClickZoom.handleEvent', + ol.interaction.DoubleClickZoom.handleEvent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.DragAndDrop', + ol.interaction.DragAndDrop, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.DragAndDrop.handleEvent', + ol.interaction.DragAndDrop.handleEvent, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.DragAndDropEvent.prototype, + 'features', + ol.interaction.DragAndDropEvent.prototype.features); + +goog.exportProperty( + ol.interaction.DragAndDropEvent.prototype, + 'file', + ol.interaction.DragAndDropEvent.prototype.file); + +goog.exportProperty( + ol.interaction.DragAndDropEvent.prototype, + 'projection', + ol.interaction.DragAndDropEvent.prototype.projection); + +goog.exportProperty( + ol.DragBoxEvent.prototype, + 'coordinate', + ol.DragBoxEvent.prototype.coordinate); + +goog.exportProperty( + ol.DragBoxEvent.prototype, + 'mapBrowserEvent', + ol.DragBoxEvent.prototype.mapBrowserEvent); + +goog.exportSymbol( + 'ol.interaction.DragBox', + ol.interaction.DragBox, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'getGeometry', + ol.interaction.DragBox.prototype.getGeometry); + +goog.exportSymbol( + 'ol.interaction.DragPan', + ol.interaction.DragPan, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.DragRotateAndZoom', + ol.interaction.DragRotateAndZoom, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.DragRotate', + ol.interaction.DragRotate, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.DragZoom', + ol.interaction.DragZoom, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.DrawEvent.prototype, + 'feature', + ol.interaction.DrawEvent.prototype.feature); + +goog.exportSymbol( + 'ol.interaction.Draw', + ol.interaction.Draw, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.Draw.handleEvent', + ol.interaction.Draw.handleEvent, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'removeLastPoint', + ol.interaction.Draw.prototype.removeLastPoint); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'finishDrawing', + ol.interaction.Draw.prototype.finishDrawing); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'extend', + ol.interaction.Draw.prototype.extend); + +goog.exportSymbol( + 'ol.interaction.Draw.createRegularPolygon', + ol.interaction.Draw.createRegularPolygon, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.Interaction', + ol.interaction.Interaction, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'getActive', + ol.interaction.Interaction.prototype.getActive); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'getMap', + ol.interaction.Interaction.prototype.getMap); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'setActive', + ol.interaction.Interaction.prototype.setActive); + +goog.exportSymbol( + 'ol.interaction.defaults', + ol.interaction.defaults, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.KeyboardPan', + ol.interaction.KeyboardPan, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.KeyboardPan.handleEvent', + ol.interaction.KeyboardPan.handleEvent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.KeyboardZoom', + ol.interaction.KeyboardZoom, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.KeyboardZoom.handleEvent', + ol.interaction.KeyboardZoom.handleEvent, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.ModifyEvent.prototype, + 'features', + ol.interaction.ModifyEvent.prototype.features); + +goog.exportProperty( + ol.interaction.ModifyEvent.prototype, + 'mapBrowserEvent', + ol.interaction.ModifyEvent.prototype.mapBrowserEvent); + +goog.exportSymbol( + 'ol.interaction.Modify', + ol.interaction.Modify, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.Modify.handleEvent', + ol.interaction.Modify.handleEvent, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'removePoint', + ol.interaction.Modify.prototype.removePoint); + +goog.exportSymbol( + 'ol.interaction.MouseWheelZoom', + ol.interaction.MouseWheelZoom, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.MouseWheelZoom.handleEvent', + ol.interaction.MouseWheelZoom.handleEvent, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'setMouseAnchor', + ol.interaction.MouseWheelZoom.prototype.setMouseAnchor); + +goog.exportSymbol( + 'ol.interaction.PinchRotate', + ol.interaction.PinchRotate, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.PinchZoom', + ol.interaction.PinchZoom, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.Pointer', + ol.interaction.Pointer, + OPENLAYERS); + +goog.exportSymbol( + 'ol.interaction.Pointer.handleEvent', + ol.interaction.Pointer.handleEvent, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.SelectEvent.prototype, + 'selected', + ol.interaction.SelectEvent.prototype.selected); + +goog.exportProperty( + ol.interaction.SelectEvent.prototype, + 'deselected', + ol.interaction.SelectEvent.prototype.deselected); + +goog.exportProperty( + ol.interaction.SelectEvent.prototype, + 'mapBrowserEvent', + ol.interaction.SelectEvent.prototype.mapBrowserEvent); + +goog.exportSymbol( + 'ol.interaction.Select', + ol.interaction.Select, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'getFeatures', + ol.interaction.Select.prototype.getFeatures); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'getLayer', + ol.interaction.Select.prototype.getLayer); + +goog.exportSymbol( + 'ol.interaction.Select.handleEvent', + ol.interaction.Select.handleEvent, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'setMap', + ol.interaction.Select.prototype.setMap); + +goog.exportSymbol( + 'ol.interaction.Snap', + ol.interaction.Snap, + OPENLAYERS); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'addFeature', + ol.interaction.Snap.prototype.addFeature); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'removeFeature', + ol.interaction.Snap.prototype.removeFeature); + +goog.exportProperty( + ol.interaction.TranslateEvent.prototype, + 'features', + ol.interaction.TranslateEvent.prototype.features); + +goog.exportProperty( + ol.interaction.TranslateEvent.prototype, + 'coordinate', + ol.interaction.TranslateEvent.prototype.coordinate); + +goog.exportSymbol( + 'ol.interaction.Translate', + ol.interaction.Translate, + OPENLAYERS); + +goog.exportSymbol( + 'ol.geom.Circle', + ol.geom.Circle, + OPENLAYERS); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'clone', + ol.geom.Circle.prototype.clone); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getCenter', + ol.geom.Circle.prototype.getCenter); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getRadius', + ol.geom.Circle.prototype.getRadius); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getType', + ol.geom.Circle.prototype.getType); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'intersectsExtent', + ol.geom.Circle.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'setCenter', + ol.geom.Circle.prototype.setCenter); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'setCenterAndRadius', + ol.geom.Circle.prototype.setCenterAndRadius); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'setRadius', + ol.geom.Circle.prototype.setRadius); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'transform', + ol.geom.Circle.prototype.transform); + +goog.exportSymbol( + 'ol.geom.Geometry', + ol.geom.Geometry, + OPENLAYERS); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'getClosestPoint', + ol.geom.Geometry.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'getExtent', + ol.geom.Geometry.prototype.getExtent); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'rotate', + ol.geom.Geometry.prototype.rotate); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'simplify', + ol.geom.Geometry.prototype.simplify); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'transform', + ol.geom.Geometry.prototype.transform); + +goog.exportSymbol( + 'ol.geom.GeometryCollection', + ol.geom.GeometryCollection, + OPENLAYERS); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'clone', + ol.geom.GeometryCollection.prototype.clone); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'getGeometries', + ol.geom.GeometryCollection.prototype.getGeometries); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'getType', + ol.geom.GeometryCollection.prototype.getType); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'intersectsExtent', + ol.geom.GeometryCollection.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'setGeometries', + ol.geom.GeometryCollection.prototype.setGeometries); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'applyTransform', + ol.geom.GeometryCollection.prototype.applyTransform); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'translate', + ol.geom.GeometryCollection.prototype.translate); + +goog.exportSymbol( + 'ol.geom.LinearRing', + ol.geom.LinearRing, + OPENLAYERS); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'clone', + ol.geom.LinearRing.prototype.clone); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getArea', + ol.geom.LinearRing.prototype.getArea); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getCoordinates', + ol.geom.LinearRing.prototype.getCoordinates); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getType', + ol.geom.LinearRing.prototype.getType); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'setCoordinates', + ol.geom.LinearRing.prototype.setCoordinates); + +goog.exportSymbol( + 'ol.geom.LineString', + ol.geom.LineString, + OPENLAYERS); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'appendCoordinate', + ol.geom.LineString.prototype.appendCoordinate); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'clone', + ol.geom.LineString.prototype.clone); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'forEachSegment', + ol.geom.LineString.prototype.forEachSegment); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getCoordinateAtM', + ol.geom.LineString.prototype.getCoordinateAtM); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getCoordinates', + ol.geom.LineString.prototype.getCoordinates); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getCoordinateAt', + ol.geom.LineString.prototype.getCoordinateAt); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getLength', + ol.geom.LineString.prototype.getLength); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getType', + ol.geom.LineString.prototype.getType); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'intersectsExtent', + ol.geom.LineString.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'setCoordinates', + ol.geom.LineString.prototype.setCoordinates); + +goog.exportSymbol( + 'ol.geom.MultiLineString', + ol.geom.MultiLineString, + OPENLAYERS); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'appendLineString', + ol.geom.MultiLineString.prototype.appendLineString); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'clone', + ol.geom.MultiLineString.prototype.clone); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getCoordinateAtM', + ol.geom.MultiLineString.prototype.getCoordinateAtM); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getCoordinates', + ol.geom.MultiLineString.prototype.getCoordinates); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getLineString', + ol.geom.MultiLineString.prototype.getLineString); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getLineStrings', + ol.geom.MultiLineString.prototype.getLineStrings); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getType', + ol.geom.MultiLineString.prototype.getType); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'intersectsExtent', + ol.geom.MultiLineString.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'setCoordinates', + ol.geom.MultiLineString.prototype.setCoordinates); + +goog.exportSymbol( + 'ol.geom.MultiPoint', + ol.geom.MultiPoint, + OPENLAYERS); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'appendPoint', + ol.geom.MultiPoint.prototype.appendPoint); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'clone', + ol.geom.MultiPoint.prototype.clone); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getCoordinates', + ol.geom.MultiPoint.prototype.getCoordinates); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getPoint', + ol.geom.MultiPoint.prototype.getPoint); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getPoints', + ol.geom.MultiPoint.prototype.getPoints); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getType', + ol.geom.MultiPoint.prototype.getType); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'intersectsExtent', + ol.geom.MultiPoint.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'setCoordinates', + ol.geom.MultiPoint.prototype.setCoordinates); + +goog.exportSymbol( + 'ol.geom.MultiPolygon', + ol.geom.MultiPolygon, + OPENLAYERS); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'appendPolygon', + ol.geom.MultiPolygon.prototype.appendPolygon); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'clone', + ol.geom.MultiPolygon.prototype.clone); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getArea', + ol.geom.MultiPolygon.prototype.getArea); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getCoordinates', + ol.geom.MultiPolygon.prototype.getCoordinates); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getInteriorPoints', + ol.geom.MultiPolygon.prototype.getInteriorPoints); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getPolygon', + ol.geom.MultiPolygon.prototype.getPolygon); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getPolygons', + ol.geom.MultiPolygon.prototype.getPolygons); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getType', + ol.geom.MultiPolygon.prototype.getType); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'intersectsExtent', + ol.geom.MultiPolygon.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'setCoordinates', + ol.geom.MultiPolygon.prototype.setCoordinates); + +goog.exportSymbol( + 'ol.geom.Point', + ol.geom.Point, + OPENLAYERS); + +goog.exportProperty( + ol.geom.Point.prototype, + 'clone', + ol.geom.Point.prototype.clone); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getCoordinates', + ol.geom.Point.prototype.getCoordinates); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getType', + ol.geom.Point.prototype.getType); + +goog.exportProperty( + ol.geom.Point.prototype, + 'intersectsExtent', + ol.geom.Point.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.Point.prototype, + 'setCoordinates', + ol.geom.Point.prototype.setCoordinates); + +goog.exportSymbol( + 'ol.geom.Polygon', + ol.geom.Polygon, + OPENLAYERS); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'appendLinearRing', + ol.geom.Polygon.prototype.appendLinearRing); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'clone', + ol.geom.Polygon.prototype.clone); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getArea', + ol.geom.Polygon.prototype.getArea); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getCoordinates', + ol.geom.Polygon.prototype.getCoordinates); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getInteriorPoint', + ol.geom.Polygon.prototype.getInteriorPoint); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getLinearRingCount', + ol.geom.Polygon.prototype.getLinearRingCount); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getLinearRing', + ol.geom.Polygon.prototype.getLinearRing); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getLinearRings', + ol.geom.Polygon.prototype.getLinearRings); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getType', + ol.geom.Polygon.prototype.getType); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'intersectsExtent', + ol.geom.Polygon.prototype.intersectsExtent); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'setCoordinates', + ol.geom.Polygon.prototype.setCoordinates); + +goog.exportSymbol( + 'ol.geom.Polygon.circular', + ol.geom.Polygon.circular, + OPENLAYERS); + +goog.exportSymbol( + 'ol.geom.Polygon.fromExtent', + ol.geom.Polygon.fromExtent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.geom.Polygon.fromCircle', + ol.geom.Polygon.fromCircle, + OPENLAYERS); + +goog.exportSymbol( + 'ol.geom.SimpleGeometry', + ol.geom.SimpleGeometry, + OPENLAYERS); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getFirstCoordinate', + ol.geom.SimpleGeometry.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getLastCoordinate', + ol.geom.SimpleGeometry.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getLayout', + ol.geom.SimpleGeometry.prototype.getLayout); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'applyTransform', + ol.geom.SimpleGeometry.prototype.applyTransform); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'translate', + ol.geom.SimpleGeometry.prototype.translate); + +goog.exportSymbol( + 'ol.format.EsriJSON', + ol.format.EsriJSON, + OPENLAYERS); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'readFeature', + ol.format.EsriJSON.prototype.readFeature); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'readFeatures', + ol.format.EsriJSON.prototype.readFeatures); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'readGeometry', + ol.format.EsriJSON.prototype.readGeometry); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'readProjection', + ol.format.EsriJSON.prototype.readProjection); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'writeGeometry', + ol.format.EsriJSON.prototype.writeGeometry); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'writeGeometryObject', + ol.format.EsriJSON.prototype.writeGeometryObject); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'writeFeature', + ol.format.EsriJSON.prototype.writeFeature); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'writeFeatureObject', + ol.format.EsriJSON.prototype.writeFeatureObject); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'writeFeatures', + ol.format.EsriJSON.prototype.writeFeatures); + +goog.exportProperty( + ol.format.EsriJSON.prototype, + 'writeFeaturesObject', + ol.format.EsriJSON.prototype.writeFeaturesObject); + +goog.exportSymbol( + 'ol.format.Feature', + ol.format.Feature, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.GeoJSON', + ol.format.GeoJSON, + OPENLAYERS); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'readFeature', + ol.format.GeoJSON.prototype.readFeature); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'readFeatures', + ol.format.GeoJSON.prototype.readFeatures); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'readGeometry', + ol.format.GeoJSON.prototype.readGeometry); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'readProjection', + ol.format.GeoJSON.prototype.readProjection); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'writeFeature', + ol.format.GeoJSON.prototype.writeFeature); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'writeFeatureObject', + ol.format.GeoJSON.prototype.writeFeatureObject); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'writeFeatures', + ol.format.GeoJSON.prototype.writeFeatures); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'writeFeaturesObject', + ol.format.GeoJSON.prototype.writeFeaturesObject); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'writeGeometry', + ol.format.GeoJSON.prototype.writeGeometry); + +goog.exportProperty( + ol.format.GeoJSON.prototype, + 'writeGeometryObject', + ol.format.GeoJSON.prototype.writeGeometryObject); + +goog.exportSymbol( + 'ol.format.GPX', + ol.format.GPX, + OPENLAYERS); + +goog.exportProperty( + ol.format.GPX.prototype, + 'readFeature', + ol.format.GPX.prototype.readFeature); + +goog.exportProperty( + ol.format.GPX.prototype, + 'readFeatures', + ol.format.GPX.prototype.readFeatures); + +goog.exportProperty( + ol.format.GPX.prototype, + 'readProjection', + ol.format.GPX.prototype.readProjection); + +goog.exportProperty( + ol.format.GPX.prototype, + 'writeFeatures', + ol.format.GPX.prototype.writeFeatures); + +goog.exportProperty( + ol.format.GPX.prototype, + 'writeFeaturesNode', + ol.format.GPX.prototype.writeFeaturesNode); + +goog.exportSymbol( + 'ol.format.IGC', + ol.format.IGC, + OPENLAYERS); + +goog.exportProperty( + ol.format.IGC.prototype, + 'readFeature', + ol.format.IGC.prototype.readFeature); + +goog.exportProperty( + ol.format.IGC.prototype, + 'readFeatures', + ol.format.IGC.prototype.readFeatures); + +goog.exportProperty( + ol.format.IGC.prototype, + 'readProjection', + ol.format.IGC.prototype.readProjection); + +goog.exportSymbol( + 'ol.format.KML', + ol.format.KML, + OPENLAYERS); + +goog.exportProperty( + ol.format.KML.prototype, + 'readFeature', + ol.format.KML.prototype.readFeature); + +goog.exportProperty( + ol.format.KML.prototype, + 'readFeatures', + ol.format.KML.prototype.readFeatures); + +goog.exportProperty( + ol.format.KML.prototype, + 'readName', + ol.format.KML.prototype.readName); + +goog.exportProperty( + ol.format.KML.prototype, + 'readNetworkLinks', + ol.format.KML.prototype.readNetworkLinks); + +goog.exportProperty( + ol.format.KML.prototype, + 'readProjection', + ol.format.KML.prototype.readProjection); + +goog.exportProperty( + ol.format.KML.prototype, + 'writeFeatures', + ol.format.KML.prototype.writeFeatures); + +goog.exportProperty( + ol.format.KML.prototype, + 'writeFeaturesNode', + ol.format.KML.prototype.writeFeaturesNode); + +goog.exportSymbol( + 'ol.format.MVT', + ol.format.MVT, + OPENLAYERS); + +goog.exportProperty( + ol.format.MVT.prototype, + 'readFeatures', + ol.format.MVT.prototype.readFeatures); + +goog.exportProperty( + ol.format.MVT.prototype, + 'readProjection', + ol.format.MVT.prototype.readProjection); + +goog.exportProperty( + ol.format.MVT.prototype, + 'setLayers', + ol.format.MVT.prototype.setLayers); + +goog.exportSymbol( + 'ol.format.OSMXML', + ol.format.OSMXML, + OPENLAYERS); + +goog.exportProperty( + ol.format.OSMXML.prototype, + 'readFeatures', + ol.format.OSMXML.prototype.readFeatures); + +goog.exportProperty( + ol.format.OSMXML.prototype, + 'readProjection', + ol.format.OSMXML.prototype.readProjection); + +goog.exportSymbol( + 'ol.format.Polyline', + ol.format.Polyline, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.Polyline.encodeDeltas', + ol.format.Polyline.encodeDeltas, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.Polyline.decodeDeltas', + ol.format.Polyline.decodeDeltas, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.Polyline.encodeFloats', + ol.format.Polyline.encodeFloats, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.Polyline.decodeFloats', + ol.format.Polyline.decodeFloats, + OPENLAYERS); + +goog.exportProperty( + ol.format.Polyline.prototype, + 'readFeature', + ol.format.Polyline.prototype.readFeature); + +goog.exportProperty( + ol.format.Polyline.prototype, + 'readFeatures', + ol.format.Polyline.prototype.readFeatures); + +goog.exportProperty( + ol.format.Polyline.prototype, + 'readGeometry', + ol.format.Polyline.prototype.readGeometry); + +goog.exportProperty( + ol.format.Polyline.prototype, + 'readProjection', + ol.format.Polyline.prototype.readProjection); + +goog.exportProperty( + ol.format.Polyline.prototype, + 'writeGeometry', + ol.format.Polyline.prototype.writeGeometry); + +goog.exportSymbol( + 'ol.format.TopoJSON', + ol.format.TopoJSON, + OPENLAYERS); + +goog.exportProperty( + ol.format.TopoJSON.prototype, + 'readFeatures', + ol.format.TopoJSON.prototype.readFeatures); + +goog.exportProperty( + ol.format.TopoJSON.prototype, + 'readProjection', + ol.format.TopoJSON.prototype.readProjection); + +goog.exportSymbol( + 'ol.format.WFS', + ol.format.WFS, + OPENLAYERS); + +goog.exportProperty( + ol.format.WFS.prototype, + 'readFeatures', + ol.format.WFS.prototype.readFeatures); + +goog.exportProperty( + ol.format.WFS.prototype, + 'readTransactionResponse', + ol.format.WFS.prototype.readTransactionResponse); + +goog.exportProperty( + ol.format.WFS.prototype, + 'readFeatureCollectionMetadata', + ol.format.WFS.prototype.readFeatureCollectionMetadata); + +goog.exportProperty( + ol.format.WFS.prototype, + 'writeGetFeature', + ol.format.WFS.prototype.writeGetFeature); + +goog.exportProperty( + ol.format.WFS.prototype, + 'writeTransaction', + ol.format.WFS.prototype.writeTransaction); + +goog.exportProperty( + ol.format.WFS.prototype, + 'readProjection', + ol.format.WFS.prototype.readProjection); + +goog.exportSymbol( + 'ol.format.WKT', + ol.format.WKT, + OPENLAYERS); + +goog.exportProperty( + ol.format.WKT.prototype, + 'readFeature', + ol.format.WKT.prototype.readFeature); + +goog.exportProperty( + ol.format.WKT.prototype, + 'readFeatures', + ol.format.WKT.prototype.readFeatures); + +goog.exportProperty( + ol.format.WKT.prototype, + 'readGeometry', + ol.format.WKT.prototype.readGeometry); + +goog.exportProperty( + ol.format.WKT.prototype, + 'writeFeature', + ol.format.WKT.prototype.writeFeature); + +goog.exportProperty( + ol.format.WKT.prototype, + 'writeFeatures', + ol.format.WKT.prototype.writeFeatures); + +goog.exportProperty( + ol.format.WKT.prototype, + 'writeGeometry', + ol.format.WKT.prototype.writeGeometry); + +goog.exportSymbol( + 'ol.format.WMSCapabilities', + ol.format.WMSCapabilities, + OPENLAYERS); + +goog.exportProperty( + ol.format.WMSCapabilities.prototype, + 'read', + ol.format.WMSCapabilities.prototype.read); + +goog.exportSymbol( + 'ol.format.WMSGetFeatureInfo', + ol.format.WMSGetFeatureInfo, + OPENLAYERS); + +goog.exportProperty( + ol.format.WMSGetFeatureInfo.prototype, + 'readFeatures', + ol.format.WMSGetFeatureInfo.prototype.readFeatures); + +goog.exportSymbol( + 'ol.format.WMTSCapabilities', + ol.format.WMTSCapabilities, + OPENLAYERS); + +goog.exportProperty( + ol.format.WMTSCapabilities.prototype, + 'read', + ol.format.WMTSCapabilities.prototype.read); + +goog.exportSymbol( + 'ol.format.ogc.filter.and', + ol.format.ogc.filter.and, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.or', + ol.format.ogc.filter.or, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.not', + ol.format.ogc.filter.not, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.bbox', + ol.format.ogc.filter.bbox, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.equalTo', + ol.format.ogc.filter.equalTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.notEqualTo', + ol.format.ogc.filter.notEqualTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.lessThan', + ol.format.ogc.filter.lessThan, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.lessThanOrEqualTo', + ol.format.ogc.filter.lessThanOrEqualTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.greaterThan', + ol.format.ogc.filter.greaterThan, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.greaterThanOrEqualTo', + ol.format.ogc.filter.greaterThanOrEqualTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.isNull', + ol.format.ogc.filter.isNull, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.between', + ol.format.ogc.filter.between, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.like', + ol.format.ogc.filter.like, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.Filter', + ol.format.ogc.filter.Filter, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.And', + ol.format.ogc.filter.And, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.Or', + ol.format.ogc.filter.Or, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.Not', + ol.format.ogc.filter.Not, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.Bbox', + ol.format.ogc.filter.Bbox, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.Comparison', + ol.format.ogc.filter.Comparison, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.ComparisonBinary', + ol.format.ogc.filter.ComparisonBinary, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.EqualTo', + ol.format.ogc.filter.EqualTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.NotEqualTo', + ol.format.ogc.filter.NotEqualTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.LessThan', + ol.format.ogc.filter.LessThan, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.LessThanOrEqualTo', + ol.format.ogc.filter.LessThanOrEqualTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.GreaterThan', + ol.format.ogc.filter.GreaterThan, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.GreaterThanOrEqualTo', + ol.format.ogc.filter.GreaterThanOrEqualTo, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.IsNull', + ol.format.ogc.filter.IsNull, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.IsBetween', + ol.format.ogc.filter.IsBetween, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.ogc.filter.IsLike', + ol.format.ogc.filter.IsLike, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.GML2', + ol.format.GML2, + OPENLAYERS); + +goog.exportSymbol( + 'ol.format.GML3', + ol.format.GML3, + OPENLAYERS); + +goog.exportProperty( + ol.format.GML3.prototype, + 'writeGeometryNode', + ol.format.GML3.prototype.writeGeometryNode); + +goog.exportProperty( + ol.format.GML3.prototype, + 'writeFeatures', + ol.format.GML3.prototype.writeFeatures); + +goog.exportProperty( + ol.format.GML3.prototype, + 'writeFeaturesNode', + ol.format.GML3.prototype.writeFeaturesNode); + +goog.exportSymbol( + 'ol.format.GML', + ol.format.GML, + OPENLAYERS); + +goog.exportProperty( + ol.format.GML.prototype, + 'writeFeatures', + ol.format.GML.prototype.writeFeatures); + +goog.exportProperty( + ol.format.GML.prototype, + 'writeFeaturesNode', + ol.format.GML.prototype.writeFeaturesNode); + +goog.exportProperty( + ol.format.GMLBase.prototype, + 'readFeatures', + ol.format.GMLBase.prototype.readFeatures); + +goog.exportSymbol( + 'ol.events.condition.altKeyOnly', + ol.events.condition.altKeyOnly, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.altShiftKeysOnly', + ol.events.condition.altShiftKeysOnly, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.always', + ol.events.condition.always, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.click', + ol.events.condition.click, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.never', + ol.events.condition.never, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.pointerMove', + ol.events.condition.pointerMove, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.singleClick', + ol.events.condition.singleClick, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.doubleClick', + ol.events.condition.doubleClick, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.noModifierKeys', + ol.events.condition.noModifierKeys, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.platformModifierKeyOnly', + ol.events.condition.platformModifierKeyOnly, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.shiftKeyOnly', + ol.events.condition.shiftKeyOnly, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.targetNotEditable', + ol.events.condition.targetNotEditable, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.mouseOnly', + ol.events.condition.mouseOnly, + OPENLAYERS); + +goog.exportSymbol( + 'ol.events.condition.primaryAction', + ol.events.condition.primaryAction, + OPENLAYERS); + +goog.exportProperty( + ol.events.Event.prototype, + 'type', + ol.events.Event.prototype.type); + +goog.exportProperty( + ol.events.Event.prototype, + 'target', + ol.events.Event.prototype.target); + +goog.exportProperty( + ol.events.Event.prototype, + 'preventDefault', + ol.events.Event.prototype.preventDefault); + +goog.exportProperty( + ol.events.Event.prototype, + 'stopPropagation', + ol.events.Event.prototype.stopPropagation); + +goog.exportSymbol( + 'ol.control.Attribution', + ol.control.Attribution, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.Attribution.render', + ol.control.Attribution.render, + OPENLAYERS); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'getCollapsible', + ol.control.Attribution.prototype.getCollapsible); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'setCollapsible', + ol.control.Attribution.prototype.setCollapsible); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'setCollapsed', + ol.control.Attribution.prototype.setCollapsed); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'getCollapsed', + ol.control.Attribution.prototype.getCollapsed); + +goog.exportSymbol( + 'ol.control.Control', + ol.control.Control, + OPENLAYERS); + +goog.exportProperty( + ol.control.Control.prototype, + 'getMap', + ol.control.Control.prototype.getMap); + +goog.exportProperty( + ol.control.Control.prototype, + 'setMap', + ol.control.Control.prototype.setMap); + +goog.exportProperty( + ol.control.Control.prototype, + 'setTarget', + ol.control.Control.prototype.setTarget); + +goog.exportSymbol( + 'ol.control.defaults', + ol.control.defaults, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.FullScreen', + ol.control.FullScreen, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.MousePosition', + ol.control.MousePosition, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.MousePosition.render', + ol.control.MousePosition.render, + OPENLAYERS); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'getCoordinateFormat', + ol.control.MousePosition.prototype.getCoordinateFormat); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'getProjection', + ol.control.MousePosition.prototype.getProjection); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'setCoordinateFormat', + ol.control.MousePosition.prototype.setCoordinateFormat); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'setProjection', + ol.control.MousePosition.prototype.setProjection); + +goog.exportSymbol( + 'ol.control.OverviewMap', + ol.control.OverviewMap, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.OverviewMap.render', + ol.control.OverviewMap.render, + OPENLAYERS); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'getCollapsible', + ol.control.OverviewMap.prototype.getCollapsible); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'setCollapsible', + ol.control.OverviewMap.prototype.setCollapsible); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'setCollapsed', + ol.control.OverviewMap.prototype.setCollapsed); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'getCollapsed', + ol.control.OverviewMap.prototype.getCollapsed); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'getOverviewMap', + ol.control.OverviewMap.prototype.getOverviewMap); + +goog.exportSymbol( + 'ol.control.Rotate', + ol.control.Rotate, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.Rotate.render', + ol.control.Rotate.render, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.ScaleLine', + ol.control.ScaleLine, + OPENLAYERS); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'getUnits', + ol.control.ScaleLine.prototype.getUnits); + +goog.exportSymbol( + 'ol.control.ScaleLine.render', + ol.control.ScaleLine.render, + OPENLAYERS); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'setUnits', + ol.control.ScaleLine.prototype.setUnits); + +goog.exportSymbol( + 'ol.control.Zoom', + ol.control.Zoom, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.ZoomSlider', + ol.control.ZoomSlider, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.ZoomSlider.render', + ol.control.ZoomSlider.render, + OPENLAYERS); + +goog.exportSymbol( + 'ol.control.ZoomToExtent', + ol.control.ZoomToExtent, + OPENLAYERS); + +goog.exportSymbol( + 'ol.color.asArray', + ol.color.asArray, + OPENLAYERS); + +goog.exportSymbol( + 'ol.color.asString', + ol.color.asString, + OPENLAYERS); + +goog.exportProperty( + ol.CollectionEvent.prototype, + 'type', + ol.CollectionEvent.prototype.type); + +goog.exportProperty( + ol.CollectionEvent.prototype, + 'target', + ol.CollectionEvent.prototype.target); + +goog.exportProperty( + ol.CollectionEvent.prototype, + 'preventDefault', + ol.CollectionEvent.prototype.preventDefault); + +goog.exportProperty( + ol.CollectionEvent.prototype, + 'stopPropagation', + ol.CollectionEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.Object.prototype, + 'changed', + ol.Object.prototype.changed); + +goog.exportProperty( + ol.Object.prototype, + 'dispatchEvent', + ol.Object.prototype.dispatchEvent); + +goog.exportProperty( + ol.Object.prototype, + 'getRevision', + ol.Object.prototype.getRevision); + +goog.exportProperty( + ol.Object.prototype, + 'on', + ol.Object.prototype.on); + +goog.exportProperty( + ol.Object.prototype, + 'once', + ol.Object.prototype.once); + +goog.exportProperty( + ol.Object.prototype, + 'un', + ol.Object.prototype.un); + +goog.exportProperty( + ol.Object.prototype, + 'unByKey', + ol.Object.prototype.unByKey); + +goog.exportProperty( + ol.Collection.prototype, + 'get', + ol.Collection.prototype.get); + +goog.exportProperty( + ol.Collection.prototype, + 'getKeys', + ol.Collection.prototype.getKeys); + +goog.exportProperty( + ol.Collection.prototype, + 'getProperties', + ol.Collection.prototype.getProperties); + +goog.exportProperty( + ol.Collection.prototype, + 'set', + ol.Collection.prototype.set); + +goog.exportProperty( + ol.Collection.prototype, + 'setProperties', + ol.Collection.prototype.setProperties); + +goog.exportProperty( + ol.Collection.prototype, + 'unset', + ol.Collection.prototype.unset); + +goog.exportProperty( + ol.Collection.prototype, + 'changed', + ol.Collection.prototype.changed); + +goog.exportProperty( + ol.Collection.prototype, + 'dispatchEvent', + ol.Collection.prototype.dispatchEvent); + +goog.exportProperty( + ol.Collection.prototype, + 'getRevision', + ol.Collection.prototype.getRevision); + +goog.exportProperty( + ol.Collection.prototype, + 'on', + ol.Collection.prototype.on); + +goog.exportProperty( + ol.Collection.prototype, + 'once', + ol.Collection.prototype.once); + +goog.exportProperty( + ol.Collection.prototype, + 'un', + ol.Collection.prototype.un); + +goog.exportProperty( + ol.Collection.prototype, + 'unByKey', + ol.Collection.prototype.unByKey); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'get', + ol.DeviceOrientation.prototype.get); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getKeys', + ol.DeviceOrientation.prototype.getKeys); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getProperties', + ol.DeviceOrientation.prototype.getProperties); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'set', + ol.DeviceOrientation.prototype.set); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'setProperties', + ol.DeviceOrientation.prototype.setProperties); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'unset', + ol.DeviceOrientation.prototype.unset); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'changed', + ol.DeviceOrientation.prototype.changed); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'dispatchEvent', + ol.DeviceOrientation.prototype.dispatchEvent); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'getRevision', + ol.DeviceOrientation.prototype.getRevision); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'on', + ol.DeviceOrientation.prototype.on); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'once', + ol.DeviceOrientation.prototype.once); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'un', + ol.DeviceOrientation.prototype.un); + +goog.exportProperty( + ol.DeviceOrientation.prototype, + 'unByKey', + ol.DeviceOrientation.prototype.unByKey); + +goog.exportProperty( + ol.Feature.prototype, + 'get', + ol.Feature.prototype.get); + +goog.exportProperty( + ol.Feature.prototype, + 'getKeys', + ol.Feature.prototype.getKeys); + +goog.exportProperty( + ol.Feature.prototype, + 'getProperties', + ol.Feature.prototype.getProperties); + +goog.exportProperty( + ol.Feature.prototype, + 'set', + ol.Feature.prototype.set); + +goog.exportProperty( + ol.Feature.prototype, + 'setProperties', + ol.Feature.prototype.setProperties); + +goog.exportProperty( + ol.Feature.prototype, + 'unset', + ol.Feature.prototype.unset); + +goog.exportProperty( + ol.Feature.prototype, + 'changed', + ol.Feature.prototype.changed); + +goog.exportProperty( + ol.Feature.prototype, + 'dispatchEvent', + ol.Feature.prototype.dispatchEvent); + +goog.exportProperty( + ol.Feature.prototype, + 'getRevision', + ol.Feature.prototype.getRevision); + +goog.exportProperty( + ol.Feature.prototype, + 'on', + ol.Feature.prototype.on); + +goog.exportProperty( + ol.Feature.prototype, + 'once', + ol.Feature.prototype.once); + +goog.exportProperty( + ol.Feature.prototype, + 'un', + ol.Feature.prototype.un); + +goog.exportProperty( + ol.Feature.prototype, + 'unByKey', + ol.Feature.prototype.unByKey); + +goog.exportProperty( + ol.Geolocation.prototype, + 'get', + ol.Geolocation.prototype.get); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getKeys', + ol.Geolocation.prototype.getKeys); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getProperties', + ol.Geolocation.prototype.getProperties); + +goog.exportProperty( + ol.Geolocation.prototype, + 'set', + ol.Geolocation.prototype.set); + +goog.exportProperty( + ol.Geolocation.prototype, + 'setProperties', + ol.Geolocation.prototype.setProperties); + +goog.exportProperty( + ol.Geolocation.prototype, + 'unset', + ol.Geolocation.prototype.unset); + +goog.exportProperty( + ol.Geolocation.prototype, + 'changed', + ol.Geolocation.prototype.changed); + +goog.exportProperty( + ol.Geolocation.prototype, + 'dispatchEvent', + ol.Geolocation.prototype.dispatchEvent); + +goog.exportProperty( + ol.Geolocation.prototype, + 'getRevision', + ol.Geolocation.prototype.getRevision); + +goog.exportProperty( + ol.Geolocation.prototype, + 'on', + ol.Geolocation.prototype.on); + +goog.exportProperty( + ol.Geolocation.prototype, + 'once', + ol.Geolocation.prototype.once); + +goog.exportProperty( + ol.Geolocation.prototype, + 'un', + ol.Geolocation.prototype.un); + +goog.exportProperty( + ol.Geolocation.prototype, + 'unByKey', + ol.Geolocation.prototype.unByKey); + +goog.exportProperty( + ol.ImageTile.prototype, + 'getTileCoord', + ol.ImageTile.prototype.getTileCoord); + +goog.exportProperty( + ol.Map.prototype, + 'get', + ol.Map.prototype.get); + +goog.exportProperty( + ol.Map.prototype, + 'getKeys', + ol.Map.prototype.getKeys); + +goog.exportProperty( + ol.Map.prototype, + 'getProperties', + ol.Map.prototype.getProperties); + +goog.exportProperty( + ol.Map.prototype, + 'set', + ol.Map.prototype.set); + +goog.exportProperty( + ol.Map.prototype, + 'setProperties', + ol.Map.prototype.setProperties); + +goog.exportProperty( + ol.Map.prototype, + 'unset', + ol.Map.prototype.unset); + +goog.exportProperty( + ol.Map.prototype, + 'changed', + ol.Map.prototype.changed); + +goog.exportProperty( + ol.Map.prototype, + 'dispatchEvent', + ol.Map.prototype.dispatchEvent); + +goog.exportProperty( + ol.Map.prototype, + 'getRevision', + ol.Map.prototype.getRevision); + +goog.exportProperty( + ol.Map.prototype, + 'on', + ol.Map.prototype.on); + +goog.exportProperty( + ol.Map.prototype, + 'once', + ol.Map.prototype.once); + +goog.exportProperty( + ol.Map.prototype, + 'un', + ol.Map.prototype.un); + +goog.exportProperty( + ol.Map.prototype, + 'unByKey', + ol.Map.prototype.unByKey); + +goog.exportProperty( + ol.MapEvent.prototype, + 'type', + ol.MapEvent.prototype.type); + +goog.exportProperty( + ol.MapEvent.prototype, + 'target', + ol.MapEvent.prototype.target); + +goog.exportProperty( + ol.MapEvent.prototype, + 'preventDefault', + ol.MapEvent.prototype.preventDefault); + +goog.exportProperty( + ol.MapEvent.prototype, + 'stopPropagation', + ol.MapEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'map', + ol.MapBrowserEvent.prototype.map); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'frameState', + ol.MapBrowserEvent.prototype.frameState); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'type', + ol.MapBrowserEvent.prototype.type); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'target', + ol.MapBrowserEvent.prototype.target); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'preventDefault', + ol.MapBrowserEvent.prototype.preventDefault); + +goog.exportProperty( + ol.MapBrowserEvent.prototype, + 'stopPropagation', + ol.MapBrowserEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'originalEvent', + ol.MapBrowserPointerEvent.prototype.originalEvent); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'pixel', + ol.MapBrowserPointerEvent.prototype.pixel); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'coordinate', + ol.MapBrowserPointerEvent.prototype.coordinate); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'dragging', + ol.MapBrowserPointerEvent.prototype.dragging); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'preventDefault', + ol.MapBrowserPointerEvent.prototype.preventDefault); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'stopPropagation', + ol.MapBrowserPointerEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'map', + ol.MapBrowserPointerEvent.prototype.map); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'frameState', + ol.MapBrowserPointerEvent.prototype.frameState); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'type', + ol.MapBrowserPointerEvent.prototype.type); + +goog.exportProperty( + ol.MapBrowserPointerEvent.prototype, + 'target', + ol.MapBrowserPointerEvent.prototype.target); + +goog.exportProperty( + ol.ObjectEvent.prototype, + 'type', + ol.ObjectEvent.prototype.type); + +goog.exportProperty( + ol.ObjectEvent.prototype, + 'target', + ol.ObjectEvent.prototype.target); + +goog.exportProperty( + ol.ObjectEvent.prototype, + 'preventDefault', + ol.ObjectEvent.prototype.preventDefault); + +goog.exportProperty( + ol.ObjectEvent.prototype, + 'stopPropagation', + ol.ObjectEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.Overlay.prototype, + 'get', + ol.Overlay.prototype.get); + +goog.exportProperty( + ol.Overlay.prototype, + 'getKeys', + ol.Overlay.prototype.getKeys); + +goog.exportProperty( + ol.Overlay.prototype, + 'getProperties', + ol.Overlay.prototype.getProperties); + +goog.exportProperty( + ol.Overlay.prototype, + 'set', + ol.Overlay.prototype.set); + +goog.exportProperty( + ol.Overlay.prototype, + 'setProperties', + ol.Overlay.prototype.setProperties); + +goog.exportProperty( + ol.Overlay.prototype, + 'unset', + ol.Overlay.prototype.unset); + +goog.exportProperty( + ol.Overlay.prototype, + 'changed', + ol.Overlay.prototype.changed); + +goog.exportProperty( + ol.Overlay.prototype, + 'dispatchEvent', + ol.Overlay.prototype.dispatchEvent); + +goog.exportProperty( + ol.Overlay.prototype, + 'getRevision', + ol.Overlay.prototype.getRevision); + +goog.exportProperty( + ol.Overlay.prototype, + 'on', + ol.Overlay.prototype.on); + +goog.exportProperty( + ol.Overlay.prototype, + 'once', + ol.Overlay.prototype.once); + +goog.exportProperty( + ol.Overlay.prototype, + 'un', + ol.Overlay.prototype.un); + +goog.exportProperty( + ol.Overlay.prototype, + 'unByKey', + ol.Overlay.prototype.unByKey); + +goog.exportProperty( + ol.VectorTile.prototype, + 'getTileCoord', + ol.VectorTile.prototype.getTileCoord); + +goog.exportProperty( + ol.View.prototype, + 'get', + ol.View.prototype.get); + +goog.exportProperty( + ol.View.prototype, + 'getKeys', + ol.View.prototype.getKeys); + +goog.exportProperty( + ol.View.prototype, + 'getProperties', + ol.View.prototype.getProperties); + +goog.exportProperty( + ol.View.prototype, + 'set', + ol.View.prototype.set); + +goog.exportProperty( + ol.View.prototype, + 'setProperties', + ol.View.prototype.setProperties); + +goog.exportProperty( + ol.View.prototype, + 'unset', + ol.View.prototype.unset); + +goog.exportProperty( + ol.View.prototype, + 'changed', + ol.View.prototype.changed); + +goog.exportProperty( + ol.View.prototype, + 'dispatchEvent', + ol.View.prototype.dispatchEvent); + +goog.exportProperty( + ol.View.prototype, + 'getRevision', + ol.View.prototype.getRevision); + +goog.exportProperty( + ol.View.prototype, + 'on', + ol.View.prototype.on); + +goog.exportProperty( + ol.View.prototype, + 'once', + ol.View.prototype.once); + +goog.exportProperty( + ol.View.prototype, + 'un', + ol.View.prototype.un); + +goog.exportProperty( + ol.View.prototype, + 'unByKey', + ol.View.prototype.unByKey); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'forEachTileCoord', + ol.tilegrid.WMTS.prototype.forEachTileCoord); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getMaxZoom', + ol.tilegrid.WMTS.prototype.getMaxZoom); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getMinZoom', + ol.tilegrid.WMTS.prototype.getMinZoom); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getOrigin', + ol.tilegrid.WMTS.prototype.getOrigin); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getResolution', + ol.tilegrid.WMTS.prototype.getResolution); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getResolutions', + ol.tilegrid.WMTS.prototype.getResolutions); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getTileCoordExtent', + ol.tilegrid.WMTS.prototype.getTileCoordExtent); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getTileCoordForCoordAndResolution', + ol.tilegrid.WMTS.prototype.getTileCoordForCoordAndResolution); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getTileCoordForCoordAndZ', + ol.tilegrid.WMTS.prototype.getTileCoordForCoordAndZ); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getTileSize', + ol.tilegrid.WMTS.prototype.getTileSize); + +goog.exportProperty( + ol.tilegrid.WMTS.prototype, + 'getZForResolution', + ol.tilegrid.WMTS.prototype.getZForResolution); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getOpacity', + ol.style.Circle.prototype.getOpacity); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getRotateWithView', + ol.style.Circle.prototype.getRotateWithView); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getRotation', + ol.style.Circle.prototype.getRotation); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getScale', + ol.style.Circle.prototype.getScale); + +goog.exportProperty( + ol.style.Circle.prototype, + 'getSnapToPixel', + ol.style.Circle.prototype.getSnapToPixel); + +goog.exportProperty( + ol.style.Circle.prototype, + 'setOpacity', + ol.style.Circle.prototype.setOpacity); + +goog.exportProperty( + ol.style.Circle.prototype, + 'setRotation', + ol.style.Circle.prototype.setRotation); + +goog.exportProperty( + ol.style.Circle.prototype, + 'setScale', + ol.style.Circle.prototype.setScale); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getOpacity', + ol.style.Icon.prototype.getOpacity); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getRotateWithView', + ol.style.Icon.prototype.getRotateWithView); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getRotation', + ol.style.Icon.prototype.getRotation); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getScale', + ol.style.Icon.prototype.getScale); + +goog.exportProperty( + ol.style.Icon.prototype, + 'getSnapToPixel', + ol.style.Icon.prototype.getSnapToPixel); + +goog.exportProperty( + ol.style.Icon.prototype, + 'setOpacity', + ol.style.Icon.prototype.setOpacity); + +goog.exportProperty( + ol.style.Icon.prototype, + 'setRotation', + ol.style.Icon.prototype.setRotation); + +goog.exportProperty( + ol.style.Icon.prototype, + 'setScale', + ol.style.Icon.prototype.setScale); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getOpacity', + ol.style.RegularShape.prototype.getOpacity); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getRotateWithView', + ol.style.RegularShape.prototype.getRotateWithView); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getRotation', + ol.style.RegularShape.prototype.getRotation); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getScale', + ol.style.RegularShape.prototype.getScale); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'getSnapToPixel', + ol.style.RegularShape.prototype.getSnapToPixel); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'setOpacity', + ol.style.RegularShape.prototype.setOpacity); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'setRotation', + ol.style.RegularShape.prototype.setRotation); + +goog.exportProperty( + ol.style.RegularShape.prototype, + 'setScale', + ol.style.RegularShape.prototype.setScale); + +goog.exportProperty( + ol.source.Source.prototype, + 'get', + ol.source.Source.prototype.get); + +goog.exportProperty( + ol.source.Source.prototype, + 'getKeys', + ol.source.Source.prototype.getKeys); + +goog.exportProperty( + ol.source.Source.prototype, + 'getProperties', + ol.source.Source.prototype.getProperties); + +goog.exportProperty( + ol.source.Source.prototype, + 'set', + ol.source.Source.prototype.set); + +goog.exportProperty( + ol.source.Source.prototype, + 'setProperties', + ol.source.Source.prototype.setProperties); + +goog.exportProperty( + ol.source.Source.prototype, + 'unset', + ol.source.Source.prototype.unset); + +goog.exportProperty( + ol.source.Source.prototype, + 'changed', + ol.source.Source.prototype.changed); + +goog.exportProperty( + ol.source.Source.prototype, + 'dispatchEvent', + ol.source.Source.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Source.prototype, + 'getRevision', + ol.source.Source.prototype.getRevision); + +goog.exportProperty( + ol.source.Source.prototype, + 'on', + ol.source.Source.prototype.on); + +goog.exportProperty( + ol.source.Source.prototype, + 'once', + ol.source.Source.prototype.once); + +goog.exportProperty( + ol.source.Source.prototype, + 'un', + ol.source.Source.prototype.un); + +goog.exportProperty( + ol.source.Source.prototype, + 'unByKey', + ol.source.Source.prototype.unByKey); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getAttributions', + ol.source.Tile.prototype.getAttributions); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getLogo', + ol.source.Tile.prototype.getLogo); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getProjection', + ol.source.Tile.prototype.getProjection); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getState', + ol.source.Tile.prototype.getState); + +goog.exportProperty( + ol.source.Tile.prototype, + 'refresh', + ol.source.Tile.prototype.refresh); + +goog.exportProperty( + ol.source.Tile.prototype, + 'setAttributions', + ol.source.Tile.prototype.setAttributions); + +goog.exportProperty( + ol.source.Tile.prototype, + 'get', + ol.source.Tile.prototype.get); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getKeys', + ol.source.Tile.prototype.getKeys); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getProperties', + ol.source.Tile.prototype.getProperties); + +goog.exportProperty( + ol.source.Tile.prototype, + 'set', + ol.source.Tile.prototype.set); + +goog.exportProperty( + ol.source.Tile.prototype, + 'setProperties', + ol.source.Tile.prototype.setProperties); + +goog.exportProperty( + ol.source.Tile.prototype, + 'unset', + ol.source.Tile.prototype.unset); + +goog.exportProperty( + ol.source.Tile.prototype, + 'changed', + ol.source.Tile.prototype.changed); + +goog.exportProperty( + ol.source.Tile.prototype, + 'dispatchEvent', + ol.source.Tile.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Tile.prototype, + 'getRevision', + ol.source.Tile.prototype.getRevision); + +goog.exportProperty( + ol.source.Tile.prototype, + 'on', + ol.source.Tile.prototype.on); + +goog.exportProperty( + ol.source.Tile.prototype, + 'once', + ol.source.Tile.prototype.once); + +goog.exportProperty( + ol.source.Tile.prototype, + 'un', + ol.source.Tile.prototype.un); + +goog.exportProperty( + ol.source.Tile.prototype, + 'unByKey', + ol.source.Tile.prototype.unByKey); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getTileGrid', + ol.source.UrlTile.prototype.getTileGrid); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'refresh', + ol.source.UrlTile.prototype.refresh); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getAttributions', + ol.source.UrlTile.prototype.getAttributions); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getLogo', + ol.source.UrlTile.prototype.getLogo); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getProjection', + ol.source.UrlTile.prototype.getProjection); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getState', + ol.source.UrlTile.prototype.getState); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'setAttributions', + ol.source.UrlTile.prototype.setAttributions); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'get', + ol.source.UrlTile.prototype.get); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getKeys', + ol.source.UrlTile.prototype.getKeys); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getProperties', + ol.source.UrlTile.prototype.getProperties); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'set', + ol.source.UrlTile.prototype.set); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'setProperties', + ol.source.UrlTile.prototype.setProperties); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'unset', + ol.source.UrlTile.prototype.unset); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'changed', + ol.source.UrlTile.prototype.changed); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'dispatchEvent', + ol.source.UrlTile.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'getRevision', + ol.source.UrlTile.prototype.getRevision); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'on', + ol.source.UrlTile.prototype.on); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'once', + ol.source.UrlTile.prototype.once); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'un', + ol.source.UrlTile.prototype.un); + +goog.exportProperty( + ol.source.UrlTile.prototype, + 'unByKey', + ol.source.UrlTile.prototype.unByKey); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getTileLoadFunction', + ol.source.TileImage.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getTileUrlFunction', + ol.source.TileImage.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getUrls', + ol.source.TileImage.prototype.getUrls); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setTileLoadFunction', + ol.source.TileImage.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setTileUrlFunction', + ol.source.TileImage.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setUrl', + ol.source.TileImage.prototype.setUrl); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setUrls', + ol.source.TileImage.prototype.setUrls); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getTileGrid', + ol.source.TileImage.prototype.getTileGrid); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'refresh', + ol.source.TileImage.prototype.refresh); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getAttributions', + ol.source.TileImage.prototype.getAttributions); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getLogo', + ol.source.TileImage.prototype.getLogo); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getProjection', + ol.source.TileImage.prototype.getProjection); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getState', + ol.source.TileImage.prototype.getState); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setAttributions', + ol.source.TileImage.prototype.setAttributions); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'get', + ol.source.TileImage.prototype.get); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getKeys', + ol.source.TileImage.prototype.getKeys); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getProperties', + ol.source.TileImage.prototype.getProperties); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'set', + ol.source.TileImage.prototype.set); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'setProperties', + ol.source.TileImage.prototype.setProperties); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'unset', + ol.source.TileImage.prototype.unset); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'changed', + ol.source.TileImage.prototype.changed); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'dispatchEvent', + ol.source.TileImage.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'getRevision', + ol.source.TileImage.prototype.getRevision); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'on', + ol.source.TileImage.prototype.on); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'once', + ol.source.TileImage.prototype.once); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'un', + ol.source.TileImage.prototype.un); + +goog.exportProperty( + ol.source.TileImage.prototype, + 'unByKey', + ol.source.TileImage.prototype.unByKey); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setRenderReprojectionEdges', + ol.source.BingMaps.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setTileGridForProjection', + ol.source.BingMaps.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getTileLoadFunction', + ol.source.BingMaps.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getTileUrlFunction', + ol.source.BingMaps.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getUrls', + ol.source.BingMaps.prototype.getUrls); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setTileLoadFunction', + ol.source.BingMaps.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setTileUrlFunction', + ol.source.BingMaps.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setUrl', + ol.source.BingMaps.prototype.setUrl); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setUrls', + ol.source.BingMaps.prototype.setUrls); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getTileGrid', + ol.source.BingMaps.prototype.getTileGrid); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'refresh', + ol.source.BingMaps.prototype.refresh); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getAttributions', + ol.source.BingMaps.prototype.getAttributions); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getLogo', + ol.source.BingMaps.prototype.getLogo); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getProjection', + ol.source.BingMaps.prototype.getProjection); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getState', + ol.source.BingMaps.prototype.getState); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setAttributions', + ol.source.BingMaps.prototype.setAttributions); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'get', + ol.source.BingMaps.prototype.get); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getKeys', + ol.source.BingMaps.prototype.getKeys); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getProperties', + ol.source.BingMaps.prototype.getProperties); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'set', + ol.source.BingMaps.prototype.set); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'setProperties', + ol.source.BingMaps.prototype.setProperties); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'unset', + ol.source.BingMaps.prototype.unset); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'changed', + ol.source.BingMaps.prototype.changed); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'dispatchEvent', + ol.source.BingMaps.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'getRevision', + ol.source.BingMaps.prototype.getRevision); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'on', + ol.source.BingMaps.prototype.on); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'once', + ol.source.BingMaps.prototype.once); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'un', + ol.source.BingMaps.prototype.un); + +goog.exportProperty( + ol.source.BingMaps.prototype, + 'unByKey', + ol.source.BingMaps.prototype.unByKey); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setRenderReprojectionEdges', + ol.source.XYZ.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setTileGridForProjection', + ol.source.XYZ.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getTileLoadFunction', + ol.source.XYZ.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getTileUrlFunction', + ol.source.XYZ.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getUrls', + ol.source.XYZ.prototype.getUrls); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setTileLoadFunction', + ol.source.XYZ.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setTileUrlFunction', + ol.source.XYZ.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setUrl', + ol.source.XYZ.prototype.setUrl); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setUrls', + ol.source.XYZ.prototype.setUrls); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getTileGrid', + ol.source.XYZ.prototype.getTileGrid); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'refresh', + ol.source.XYZ.prototype.refresh); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getAttributions', + ol.source.XYZ.prototype.getAttributions); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getLogo', + ol.source.XYZ.prototype.getLogo); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getProjection', + ol.source.XYZ.prototype.getProjection); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getState', + ol.source.XYZ.prototype.getState); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setAttributions', + ol.source.XYZ.prototype.setAttributions); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'get', + ol.source.XYZ.prototype.get); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getKeys', + ol.source.XYZ.prototype.getKeys); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getProperties', + ol.source.XYZ.prototype.getProperties); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'set', + ol.source.XYZ.prototype.set); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'setProperties', + ol.source.XYZ.prototype.setProperties); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'unset', + ol.source.XYZ.prototype.unset); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'changed', + ol.source.XYZ.prototype.changed); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'dispatchEvent', + ol.source.XYZ.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'getRevision', + ol.source.XYZ.prototype.getRevision); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'on', + ol.source.XYZ.prototype.on); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'once', + ol.source.XYZ.prototype.once); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'un', + ol.source.XYZ.prototype.un); + +goog.exportProperty( + ol.source.XYZ.prototype, + 'unByKey', + ol.source.XYZ.prototype.unByKey); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setRenderReprojectionEdges', + ol.source.CartoDB.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setTileGridForProjection', + ol.source.CartoDB.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getTileLoadFunction', + ol.source.CartoDB.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getTileUrlFunction', + ol.source.CartoDB.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getUrls', + ol.source.CartoDB.prototype.getUrls); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setTileLoadFunction', + ol.source.CartoDB.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setTileUrlFunction', + ol.source.CartoDB.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setUrl', + ol.source.CartoDB.prototype.setUrl); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setUrls', + ol.source.CartoDB.prototype.setUrls); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getTileGrid', + ol.source.CartoDB.prototype.getTileGrid); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'refresh', + ol.source.CartoDB.prototype.refresh); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getAttributions', + ol.source.CartoDB.prototype.getAttributions); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getLogo', + ol.source.CartoDB.prototype.getLogo); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getProjection', + ol.source.CartoDB.prototype.getProjection); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getState', + ol.source.CartoDB.prototype.getState); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setAttributions', + ol.source.CartoDB.prototype.setAttributions); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'get', + ol.source.CartoDB.prototype.get); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getKeys', + ol.source.CartoDB.prototype.getKeys); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getProperties', + ol.source.CartoDB.prototype.getProperties); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'set', + ol.source.CartoDB.prototype.set); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'setProperties', + ol.source.CartoDB.prototype.setProperties); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'unset', + ol.source.CartoDB.prototype.unset); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'changed', + ol.source.CartoDB.prototype.changed); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'dispatchEvent', + ol.source.CartoDB.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'getRevision', + ol.source.CartoDB.prototype.getRevision); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'on', + ol.source.CartoDB.prototype.on); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'once', + ol.source.CartoDB.prototype.once); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'un', + ol.source.CartoDB.prototype.un); + +goog.exportProperty( + ol.source.CartoDB.prototype, + 'unByKey', + ol.source.CartoDB.prototype.unByKey); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getAttributions', + ol.source.Vector.prototype.getAttributions); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getLogo', + ol.source.Vector.prototype.getLogo); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getProjection', + ol.source.Vector.prototype.getProjection); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getState', + ol.source.Vector.prototype.getState); + +goog.exportProperty( + ol.source.Vector.prototype, + 'refresh', + ol.source.Vector.prototype.refresh); + +goog.exportProperty( + ol.source.Vector.prototype, + 'setAttributions', + ol.source.Vector.prototype.setAttributions); + +goog.exportProperty( + ol.source.Vector.prototype, + 'get', + ol.source.Vector.prototype.get); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getKeys', + ol.source.Vector.prototype.getKeys); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getProperties', + ol.source.Vector.prototype.getProperties); + +goog.exportProperty( + ol.source.Vector.prototype, + 'set', + ol.source.Vector.prototype.set); + +goog.exportProperty( + ol.source.Vector.prototype, + 'setProperties', + ol.source.Vector.prototype.setProperties); + +goog.exportProperty( + ol.source.Vector.prototype, + 'unset', + ol.source.Vector.prototype.unset); + +goog.exportProperty( + ol.source.Vector.prototype, + 'changed', + ol.source.Vector.prototype.changed); + +goog.exportProperty( + ol.source.Vector.prototype, + 'dispatchEvent', + ol.source.Vector.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Vector.prototype, + 'getRevision', + ol.source.Vector.prototype.getRevision); + +goog.exportProperty( + ol.source.Vector.prototype, + 'on', + ol.source.Vector.prototype.on); + +goog.exportProperty( + ol.source.Vector.prototype, + 'once', + ol.source.Vector.prototype.once); + +goog.exportProperty( + ol.source.Vector.prototype, + 'un', + ol.source.Vector.prototype.un); + +goog.exportProperty( + ol.source.Vector.prototype, + 'unByKey', + ol.source.Vector.prototype.unByKey); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'addFeature', + ol.source.Cluster.prototype.addFeature); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'addFeatures', + ol.source.Cluster.prototype.addFeatures); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'clear', + ol.source.Cluster.prototype.clear); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'forEachFeature', + ol.source.Cluster.prototype.forEachFeature); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'forEachFeatureInExtent', + ol.source.Cluster.prototype.forEachFeatureInExtent); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'forEachFeatureIntersectingExtent', + ol.source.Cluster.prototype.forEachFeatureIntersectingExtent); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getFeaturesCollection', + ol.source.Cluster.prototype.getFeaturesCollection); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getFeatures', + ol.source.Cluster.prototype.getFeatures); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getFeaturesAtCoordinate', + ol.source.Cluster.prototype.getFeaturesAtCoordinate); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getFeaturesInExtent', + ol.source.Cluster.prototype.getFeaturesInExtent); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getClosestFeatureToCoordinate', + ol.source.Cluster.prototype.getClosestFeatureToCoordinate); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getExtent', + ol.source.Cluster.prototype.getExtent); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getFeatureById', + ol.source.Cluster.prototype.getFeatureById); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getFormat', + ol.source.Cluster.prototype.getFormat); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getUrl', + ol.source.Cluster.prototype.getUrl); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'removeFeature', + ol.source.Cluster.prototype.removeFeature); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getAttributions', + ol.source.Cluster.prototype.getAttributions); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getLogo', + ol.source.Cluster.prototype.getLogo); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getProjection', + ol.source.Cluster.prototype.getProjection); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getState', + ol.source.Cluster.prototype.getState); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'refresh', + ol.source.Cluster.prototype.refresh); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'setAttributions', + ol.source.Cluster.prototype.setAttributions); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'get', + ol.source.Cluster.prototype.get); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getKeys', + ol.source.Cluster.prototype.getKeys); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getProperties', + ol.source.Cluster.prototype.getProperties); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'set', + ol.source.Cluster.prototype.set); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'setProperties', + ol.source.Cluster.prototype.setProperties); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'unset', + ol.source.Cluster.prototype.unset); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'changed', + ol.source.Cluster.prototype.changed); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'dispatchEvent', + ol.source.Cluster.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'getRevision', + ol.source.Cluster.prototype.getRevision); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'on', + ol.source.Cluster.prototype.on); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'once', + ol.source.Cluster.prototype.once); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'un', + ol.source.Cluster.prototype.un); + +goog.exportProperty( + ol.source.Cluster.prototype, + 'unByKey', + ol.source.Cluster.prototype.unByKey); + +goog.exportProperty( + ol.source.Image.prototype, + 'getAttributions', + ol.source.Image.prototype.getAttributions); + +goog.exportProperty( + ol.source.Image.prototype, + 'getLogo', + ol.source.Image.prototype.getLogo); + +goog.exportProperty( + ol.source.Image.prototype, + 'getProjection', + ol.source.Image.prototype.getProjection); + +goog.exportProperty( + ol.source.Image.prototype, + 'getState', + ol.source.Image.prototype.getState); + +goog.exportProperty( + ol.source.Image.prototype, + 'refresh', + ol.source.Image.prototype.refresh); + +goog.exportProperty( + ol.source.Image.prototype, + 'setAttributions', + ol.source.Image.prototype.setAttributions); + +goog.exportProperty( + ol.source.Image.prototype, + 'get', + ol.source.Image.prototype.get); + +goog.exportProperty( + ol.source.Image.prototype, + 'getKeys', + ol.source.Image.prototype.getKeys); + +goog.exportProperty( + ol.source.Image.prototype, + 'getProperties', + ol.source.Image.prototype.getProperties); + +goog.exportProperty( + ol.source.Image.prototype, + 'set', + ol.source.Image.prototype.set); + +goog.exportProperty( + ol.source.Image.prototype, + 'setProperties', + ol.source.Image.prototype.setProperties); + +goog.exportProperty( + ol.source.Image.prototype, + 'unset', + ol.source.Image.prototype.unset); + +goog.exportProperty( + ol.source.Image.prototype, + 'changed', + ol.source.Image.prototype.changed); + +goog.exportProperty( + ol.source.Image.prototype, + 'dispatchEvent', + ol.source.Image.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Image.prototype, + 'getRevision', + ol.source.Image.prototype.getRevision); + +goog.exportProperty( + ol.source.Image.prototype, + 'on', + ol.source.Image.prototype.on); + +goog.exportProperty( + ol.source.Image.prototype, + 'once', + ol.source.Image.prototype.once); + +goog.exportProperty( + ol.source.Image.prototype, + 'un', + ol.source.Image.prototype.un); + +goog.exportProperty( + ol.source.Image.prototype, + 'unByKey', + ol.source.Image.prototype.unByKey); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getAttributions', + ol.source.ImageArcGISRest.prototype.getAttributions); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getLogo', + ol.source.ImageArcGISRest.prototype.getLogo); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getProjection', + ol.source.ImageArcGISRest.prototype.getProjection); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getState', + ol.source.ImageArcGISRest.prototype.getState); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'refresh', + ol.source.ImageArcGISRest.prototype.refresh); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'setAttributions', + ol.source.ImageArcGISRest.prototype.setAttributions); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'get', + ol.source.ImageArcGISRest.prototype.get); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getKeys', + ol.source.ImageArcGISRest.prototype.getKeys); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getProperties', + ol.source.ImageArcGISRest.prototype.getProperties); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'set', + ol.source.ImageArcGISRest.prototype.set); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'setProperties', + ol.source.ImageArcGISRest.prototype.setProperties); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'unset', + ol.source.ImageArcGISRest.prototype.unset); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'changed', + ol.source.ImageArcGISRest.prototype.changed); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'dispatchEvent', + ol.source.ImageArcGISRest.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'getRevision', + ol.source.ImageArcGISRest.prototype.getRevision); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'on', + ol.source.ImageArcGISRest.prototype.on); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'once', + ol.source.ImageArcGISRest.prototype.once); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'un', + ol.source.ImageArcGISRest.prototype.un); + +goog.exportProperty( + ol.source.ImageArcGISRest.prototype, + 'unByKey', + ol.source.ImageArcGISRest.prototype.unByKey); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'getAttributions', + ol.source.ImageCanvas.prototype.getAttributions); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'getLogo', + ol.source.ImageCanvas.prototype.getLogo); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'getProjection', + ol.source.ImageCanvas.prototype.getProjection); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'getState', + ol.source.ImageCanvas.prototype.getState); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'refresh', + ol.source.ImageCanvas.prototype.refresh); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'setAttributions', + ol.source.ImageCanvas.prototype.setAttributions); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'get', + ol.source.ImageCanvas.prototype.get); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'getKeys', + ol.source.ImageCanvas.prototype.getKeys); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'getProperties', + ol.source.ImageCanvas.prototype.getProperties); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'set', + ol.source.ImageCanvas.prototype.set); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'setProperties', + ol.source.ImageCanvas.prototype.setProperties); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'unset', + ol.source.ImageCanvas.prototype.unset); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'changed', + ol.source.ImageCanvas.prototype.changed); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'dispatchEvent', + ol.source.ImageCanvas.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'getRevision', + ol.source.ImageCanvas.prototype.getRevision); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'on', + ol.source.ImageCanvas.prototype.on); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'once', + ol.source.ImageCanvas.prototype.once); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'un', + ol.source.ImageCanvas.prototype.un); + +goog.exportProperty( + ol.source.ImageCanvas.prototype, + 'unByKey', + ol.source.ImageCanvas.prototype.unByKey); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getAttributions', + ol.source.ImageMapGuide.prototype.getAttributions); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getLogo', + ol.source.ImageMapGuide.prototype.getLogo); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getProjection', + ol.source.ImageMapGuide.prototype.getProjection); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getState', + ol.source.ImageMapGuide.prototype.getState); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'refresh', + ol.source.ImageMapGuide.prototype.refresh); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'setAttributions', + ol.source.ImageMapGuide.prototype.setAttributions); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'get', + ol.source.ImageMapGuide.prototype.get); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getKeys', + ol.source.ImageMapGuide.prototype.getKeys); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getProperties', + ol.source.ImageMapGuide.prototype.getProperties); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'set', + ol.source.ImageMapGuide.prototype.set); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'setProperties', + ol.source.ImageMapGuide.prototype.setProperties); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'unset', + ol.source.ImageMapGuide.prototype.unset); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'changed', + ol.source.ImageMapGuide.prototype.changed); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'dispatchEvent', + ol.source.ImageMapGuide.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'getRevision', + ol.source.ImageMapGuide.prototype.getRevision); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'on', + ol.source.ImageMapGuide.prototype.on); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'once', + ol.source.ImageMapGuide.prototype.once); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'un', + ol.source.ImageMapGuide.prototype.un); + +goog.exportProperty( + ol.source.ImageMapGuide.prototype, + 'unByKey', + ol.source.ImageMapGuide.prototype.unByKey); + +goog.exportProperty( + ol.source.ImageEvent.prototype, + 'type', + ol.source.ImageEvent.prototype.type); + +goog.exportProperty( + ol.source.ImageEvent.prototype, + 'target', + ol.source.ImageEvent.prototype.target); + +goog.exportProperty( + ol.source.ImageEvent.prototype, + 'preventDefault', + ol.source.ImageEvent.prototype.preventDefault); + +goog.exportProperty( + ol.source.ImageEvent.prototype, + 'stopPropagation', + ol.source.ImageEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'getAttributions', + ol.source.ImageStatic.prototype.getAttributions); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'getLogo', + ol.source.ImageStatic.prototype.getLogo); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'getProjection', + ol.source.ImageStatic.prototype.getProjection); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'getState', + ol.source.ImageStatic.prototype.getState); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'refresh', + ol.source.ImageStatic.prototype.refresh); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'setAttributions', + ol.source.ImageStatic.prototype.setAttributions); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'get', + ol.source.ImageStatic.prototype.get); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'getKeys', + ol.source.ImageStatic.prototype.getKeys); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'getProperties', + ol.source.ImageStatic.prototype.getProperties); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'set', + ol.source.ImageStatic.prototype.set); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'setProperties', + ol.source.ImageStatic.prototype.setProperties); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'unset', + ol.source.ImageStatic.prototype.unset); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'changed', + ol.source.ImageStatic.prototype.changed); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'dispatchEvent', + ol.source.ImageStatic.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'getRevision', + ol.source.ImageStatic.prototype.getRevision); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'on', + ol.source.ImageStatic.prototype.on); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'once', + ol.source.ImageStatic.prototype.once); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'un', + ol.source.ImageStatic.prototype.un); + +goog.exportProperty( + ol.source.ImageStatic.prototype, + 'unByKey', + ol.source.ImageStatic.prototype.unByKey); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getAttributions', + ol.source.ImageVector.prototype.getAttributions); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getLogo', + ol.source.ImageVector.prototype.getLogo); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getProjection', + ol.source.ImageVector.prototype.getProjection); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getState', + ol.source.ImageVector.prototype.getState); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'refresh', + ol.source.ImageVector.prototype.refresh); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'setAttributions', + ol.source.ImageVector.prototype.setAttributions); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'get', + ol.source.ImageVector.prototype.get); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getKeys', + ol.source.ImageVector.prototype.getKeys); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getProperties', + ol.source.ImageVector.prototype.getProperties); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'set', + ol.source.ImageVector.prototype.set); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'setProperties', + ol.source.ImageVector.prototype.setProperties); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'unset', + ol.source.ImageVector.prototype.unset); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'changed', + ol.source.ImageVector.prototype.changed); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'dispatchEvent', + ol.source.ImageVector.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'getRevision', + ol.source.ImageVector.prototype.getRevision); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'on', + ol.source.ImageVector.prototype.on); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'once', + ol.source.ImageVector.prototype.once); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'un', + ol.source.ImageVector.prototype.un); + +goog.exportProperty( + ol.source.ImageVector.prototype, + 'unByKey', + ol.source.ImageVector.prototype.unByKey); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getAttributions', + ol.source.ImageWMS.prototype.getAttributions); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getLogo', + ol.source.ImageWMS.prototype.getLogo); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getProjection', + ol.source.ImageWMS.prototype.getProjection); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getState', + ol.source.ImageWMS.prototype.getState); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'refresh', + ol.source.ImageWMS.prototype.refresh); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'setAttributions', + ol.source.ImageWMS.prototype.setAttributions); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'get', + ol.source.ImageWMS.prototype.get); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getKeys', + ol.source.ImageWMS.prototype.getKeys); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getProperties', + ol.source.ImageWMS.prototype.getProperties); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'set', + ol.source.ImageWMS.prototype.set); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'setProperties', + ol.source.ImageWMS.prototype.setProperties); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'unset', + ol.source.ImageWMS.prototype.unset); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'changed', + ol.source.ImageWMS.prototype.changed); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'dispatchEvent', + ol.source.ImageWMS.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'getRevision', + ol.source.ImageWMS.prototype.getRevision); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'on', + ol.source.ImageWMS.prototype.on); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'once', + ol.source.ImageWMS.prototype.once); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'un', + ol.source.ImageWMS.prototype.un); + +goog.exportProperty( + ol.source.ImageWMS.prototype, + 'unByKey', + ol.source.ImageWMS.prototype.unByKey); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setRenderReprojectionEdges', + ol.source.OSM.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setTileGridForProjection', + ol.source.OSM.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getTileLoadFunction', + ol.source.OSM.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getTileUrlFunction', + ol.source.OSM.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getUrls', + ol.source.OSM.prototype.getUrls); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setTileLoadFunction', + ol.source.OSM.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setTileUrlFunction', + ol.source.OSM.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setUrl', + ol.source.OSM.prototype.setUrl); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setUrls', + ol.source.OSM.prototype.setUrls); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getTileGrid', + ol.source.OSM.prototype.getTileGrid); + +goog.exportProperty( + ol.source.OSM.prototype, + 'refresh', + ol.source.OSM.prototype.refresh); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getAttributions', + ol.source.OSM.prototype.getAttributions); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getLogo', + ol.source.OSM.prototype.getLogo); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getProjection', + ol.source.OSM.prototype.getProjection); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getState', + ol.source.OSM.prototype.getState); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setAttributions', + ol.source.OSM.prototype.setAttributions); + +goog.exportProperty( + ol.source.OSM.prototype, + 'get', + ol.source.OSM.prototype.get); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getKeys', + ol.source.OSM.prototype.getKeys); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getProperties', + ol.source.OSM.prototype.getProperties); + +goog.exportProperty( + ol.source.OSM.prototype, + 'set', + ol.source.OSM.prototype.set); + +goog.exportProperty( + ol.source.OSM.prototype, + 'setProperties', + ol.source.OSM.prototype.setProperties); + +goog.exportProperty( + ol.source.OSM.prototype, + 'unset', + ol.source.OSM.prototype.unset); + +goog.exportProperty( + ol.source.OSM.prototype, + 'changed', + ol.source.OSM.prototype.changed); + +goog.exportProperty( + ol.source.OSM.prototype, + 'dispatchEvent', + ol.source.OSM.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.OSM.prototype, + 'getRevision', + ol.source.OSM.prototype.getRevision); + +goog.exportProperty( + ol.source.OSM.prototype, + 'on', + ol.source.OSM.prototype.on); + +goog.exportProperty( + ol.source.OSM.prototype, + 'once', + ol.source.OSM.prototype.once); + +goog.exportProperty( + ol.source.OSM.prototype, + 'un', + ol.source.OSM.prototype.un); + +goog.exportProperty( + ol.source.OSM.prototype, + 'unByKey', + ol.source.OSM.prototype.unByKey); + +goog.exportProperty( + ol.source.Raster.prototype, + 'getAttributions', + ol.source.Raster.prototype.getAttributions); + +goog.exportProperty( + ol.source.Raster.prototype, + 'getLogo', + ol.source.Raster.prototype.getLogo); + +goog.exportProperty( + ol.source.Raster.prototype, + 'getProjection', + ol.source.Raster.prototype.getProjection); + +goog.exportProperty( + ol.source.Raster.prototype, + 'getState', + ol.source.Raster.prototype.getState); + +goog.exportProperty( + ol.source.Raster.prototype, + 'refresh', + ol.source.Raster.prototype.refresh); + +goog.exportProperty( + ol.source.Raster.prototype, + 'setAttributions', + ol.source.Raster.prototype.setAttributions); + +goog.exportProperty( + ol.source.Raster.prototype, + 'get', + ol.source.Raster.prototype.get); + +goog.exportProperty( + ol.source.Raster.prototype, + 'getKeys', + ol.source.Raster.prototype.getKeys); + +goog.exportProperty( + ol.source.Raster.prototype, + 'getProperties', + ol.source.Raster.prototype.getProperties); + +goog.exportProperty( + ol.source.Raster.prototype, + 'set', + ol.source.Raster.prototype.set); + +goog.exportProperty( + ol.source.Raster.prototype, + 'setProperties', + ol.source.Raster.prototype.setProperties); + +goog.exportProperty( + ol.source.Raster.prototype, + 'unset', + ol.source.Raster.prototype.unset); + +goog.exportProperty( + ol.source.Raster.prototype, + 'changed', + ol.source.Raster.prototype.changed); + +goog.exportProperty( + ol.source.Raster.prototype, + 'dispatchEvent', + ol.source.Raster.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Raster.prototype, + 'getRevision', + ol.source.Raster.prototype.getRevision); + +goog.exportProperty( + ol.source.Raster.prototype, + 'on', + ol.source.Raster.prototype.on); + +goog.exportProperty( + ol.source.Raster.prototype, + 'once', + ol.source.Raster.prototype.once); + +goog.exportProperty( + ol.source.Raster.prototype, + 'un', + ol.source.Raster.prototype.un); + +goog.exportProperty( + ol.source.Raster.prototype, + 'unByKey', + ol.source.Raster.prototype.unByKey); + +goog.exportProperty( + ol.source.RasterEvent.prototype, + 'type', + ol.source.RasterEvent.prototype.type); + +goog.exportProperty( + ol.source.RasterEvent.prototype, + 'target', + ol.source.RasterEvent.prototype.target); + +goog.exportProperty( + ol.source.RasterEvent.prototype, + 'preventDefault', + ol.source.RasterEvent.prototype.preventDefault); + +goog.exportProperty( + ol.source.RasterEvent.prototype, + 'stopPropagation', + ol.source.RasterEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setRenderReprojectionEdges', + ol.source.Stamen.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setTileGridForProjection', + ol.source.Stamen.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getTileLoadFunction', + ol.source.Stamen.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getTileUrlFunction', + ol.source.Stamen.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getUrls', + ol.source.Stamen.prototype.getUrls); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setTileLoadFunction', + ol.source.Stamen.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setTileUrlFunction', + ol.source.Stamen.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setUrl', + ol.source.Stamen.prototype.setUrl); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setUrls', + ol.source.Stamen.prototype.setUrls); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getTileGrid', + ol.source.Stamen.prototype.getTileGrid); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'refresh', + ol.source.Stamen.prototype.refresh); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getAttributions', + ol.source.Stamen.prototype.getAttributions); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getLogo', + ol.source.Stamen.prototype.getLogo); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getProjection', + ol.source.Stamen.prototype.getProjection); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getState', + ol.source.Stamen.prototype.getState); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setAttributions', + ol.source.Stamen.prototype.setAttributions); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'get', + ol.source.Stamen.prototype.get); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getKeys', + ol.source.Stamen.prototype.getKeys); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getProperties', + ol.source.Stamen.prototype.getProperties); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'set', + ol.source.Stamen.prototype.set); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'setProperties', + ol.source.Stamen.prototype.setProperties); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'unset', + ol.source.Stamen.prototype.unset); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'changed', + ol.source.Stamen.prototype.changed); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'dispatchEvent', + ol.source.Stamen.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'getRevision', + ol.source.Stamen.prototype.getRevision); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'on', + ol.source.Stamen.prototype.on); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'once', + ol.source.Stamen.prototype.once); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'un', + ol.source.Stamen.prototype.un); + +goog.exportProperty( + ol.source.Stamen.prototype, + 'unByKey', + ol.source.Stamen.prototype.unByKey); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setRenderReprojectionEdges', + ol.source.TileArcGISRest.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setTileGridForProjection', + ol.source.TileArcGISRest.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getTileLoadFunction', + ol.source.TileArcGISRest.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getTileUrlFunction', + ol.source.TileArcGISRest.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getUrls', + ol.source.TileArcGISRest.prototype.getUrls); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setTileLoadFunction', + ol.source.TileArcGISRest.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setTileUrlFunction', + ol.source.TileArcGISRest.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setUrl', + ol.source.TileArcGISRest.prototype.setUrl); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setUrls', + ol.source.TileArcGISRest.prototype.setUrls); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getTileGrid', + ol.source.TileArcGISRest.prototype.getTileGrid); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'refresh', + ol.source.TileArcGISRest.prototype.refresh); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getAttributions', + ol.source.TileArcGISRest.prototype.getAttributions); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getLogo', + ol.source.TileArcGISRest.prototype.getLogo); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getProjection', + ol.source.TileArcGISRest.prototype.getProjection); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getState', + ol.source.TileArcGISRest.prototype.getState); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setAttributions', + ol.source.TileArcGISRest.prototype.setAttributions); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'get', + ol.source.TileArcGISRest.prototype.get); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getKeys', + ol.source.TileArcGISRest.prototype.getKeys); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getProperties', + ol.source.TileArcGISRest.prototype.getProperties); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'set', + ol.source.TileArcGISRest.prototype.set); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'setProperties', + ol.source.TileArcGISRest.prototype.setProperties); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'unset', + ol.source.TileArcGISRest.prototype.unset); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'changed', + ol.source.TileArcGISRest.prototype.changed); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'dispatchEvent', + ol.source.TileArcGISRest.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'getRevision', + ol.source.TileArcGISRest.prototype.getRevision); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'on', + ol.source.TileArcGISRest.prototype.on); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'once', + ol.source.TileArcGISRest.prototype.once); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'un', + ol.source.TileArcGISRest.prototype.un); + +goog.exportProperty( + ol.source.TileArcGISRest.prototype, + 'unByKey', + ol.source.TileArcGISRest.prototype.unByKey); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getTileGrid', + ol.source.TileDebug.prototype.getTileGrid); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'refresh', + ol.source.TileDebug.prototype.refresh); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getAttributions', + ol.source.TileDebug.prototype.getAttributions); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getLogo', + ol.source.TileDebug.prototype.getLogo); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getProjection', + ol.source.TileDebug.prototype.getProjection); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getState', + ol.source.TileDebug.prototype.getState); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'setAttributions', + ol.source.TileDebug.prototype.setAttributions); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'get', + ol.source.TileDebug.prototype.get); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getKeys', + ol.source.TileDebug.prototype.getKeys); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getProperties', + ol.source.TileDebug.prototype.getProperties); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'set', + ol.source.TileDebug.prototype.set); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'setProperties', + ol.source.TileDebug.prototype.setProperties); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'unset', + ol.source.TileDebug.prototype.unset); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'changed', + ol.source.TileDebug.prototype.changed); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'dispatchEvent', + ol.source.TileDebug.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'getRevision', + ol.source.TileDebug.prototype.getRevision); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'on', + ol.source.TileDebug.prototype.on); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'once', + ol.source.TileDebug.prototype.once); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'un', + ol.source.TileDebug.prototype.un); + +goog.exportProperty( + ol.source.TileDebug.prototype, + 'unByKey', + ol.source.TileDebug.prototype.unByKey); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setRenderReprojectionEdges', + ol.source.TileJSON.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setTileGridForProjection', + ol.source.TileJSON.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getTileLoadFunction', + ol.source.TileJSON.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getTileUrlFunction', + ol.source.TileJSON.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getUrls', + ol.source.TileJSON.prototype.getUrls); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setTileLoadFunction', + ol.source.TileJSON.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setTileUrlFunction', + ol.source.TileJSON.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setUrl', + ol.source.TileJSON.prototype.setUrl); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setUrls', + ol.source.TileJSON.prototype.setUrls); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getTileGrid', + ol.source.TileJSON.prototype.getTileGrid); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'refresh', + ol.source.TileJSON.prototype.refresh); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getAttributions', + ol.source.TileJSON.prototype.getAttributions); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getLogo', + ol.source.TileJSON.prototype.getLogo); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getProjection', + ol.source.TileJSON.prototype.getProjection); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getState', + ol.source.TileJSON.prototype.getState); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setAttributions', + ol.source.TileJSON.prototype.setAttributions); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'get', + ol.source.TileJSON.prototype.get); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getKeys', + ol.source.TileJSON.prototype.getKeys); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getProperties', + ol.source.TileJSON.prototype.getProperties); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'set', + ol.source.TileJSON.prototype.set); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'setProperties', + ol.source.TileJSON.prototype.setProperties); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'unset', + ol.source.TileJSON.prototype.unset); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'changed', + ol.source.TileJSON.prototype.changed); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'dispatchEvent', + ol.source.TileJSON.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'getRevision', + ol.source.TileJSON.prototype.getRevision); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'on', + ol.source.TileJSON.prototype.on); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'once', + ol.source.TileJSON.prototype.once); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'un', + ol.source.TileJSON.prototype.un); + +goog.exportProperty( + ol.source.TileJSON.prototype, + 'unByKey', + ol.source.TileJSON.prototype.unByKey); + +goog.exportProperty( + ol.source.TileEvent.prototype, + 'type', + ol.source.TileEvent.prototype.type); + +goog.exportProperty( + ol.source.TileEvent.prototype, + 'target', + ol.source.TileEvent.prototype.target); + +goog.exportProperty( + ol.source.TileEvent.prototype, + 'preventDefault', + ol.source.TileEvent.prototype.preventDefault); + +goog.exportProperty( + ol.source.TileEvent.prototype, + 'stopPropagation', + ol.source.TileEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getTileGrid', + ol.source.TileUTFGrid.prototype.getTileGrid); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'refresh', + ol.source.TileUTFGrid.prototype.refresh); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getAttributions', + ol.source.TileUTFGrid.prototype.getAttributions); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getLogo', + ol.source.TileUTFGrid.prototype.getLogo); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getProjection', + ol.source.TileUTFGrid.prototype.getProjection); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getState', + ol.source.TileUTFGrid.prototype.getState); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'setAttributions', + ol.source.TileUTFGrid.prototype.setAttributions); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'get', + ol.source.TileUTFGrid.prototype.get); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getKeys', + ol.source.TileUTFGrid.prototype.getKeys); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getProperties', + ol.source.TileUTFGrid.prototype.getProperties); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'set', + ol.source.TileUTFGrid.prototype.set); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'setProperties', + ol.source.TileUTFGrid.prototype.setProperties); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'unset', + ol.source.TileUTFGrid.prototype.unset); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'changed', + ol.source.TileUTFGrid.prototype.changed); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'dispatchEvent', + ol.source.TileUTFGrid.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'getRevision', + ol.source.TileUTFGrid.prototype.getRevision); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'on', + ol.source.TileUTFGrid.prototype.on); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'once', + ol.source.TileUTFGrid.prototype.once); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'un', + ol.source.TileUTFGrid.prototype.un); + +goog.exportProperty( + ol.source.TileUTFGrid.prototype, + 'unByKey', + ol.source.TileUTFGrid.prototype.unByKey); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setRenderReprojectionEdges', + ol.source.TileWMS.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setTileGridForProjection', + ol.source.TileWMS.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getTileLoadFunction', + ol.source.TileWMS.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getTileUrlFunction', + ol.source.TileWMS.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getUrls', + ol.source.TileWMS.prototype.getUrls); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setTileLoadFunction', + ol.source.TileWMS.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setTileUrlFunction', + ol.source.TileWMS.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setUrl', + ol.source.TileWMS.prototype.setUrl); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setUrls', + ol.source.TileWMS.prototype.setUrls); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getTileGrid', + ol.source.TileWMS.prototype.getTileGrid); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'refresh', + ol.source.TileWMS.prototype.refresh); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getAttributions', + ol.source.TileWMS.prototype.getAttributions); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getLogo', + ol.source.TileWMS.prototype.getLogo); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getProjection', + ol.source.TileWMS.prototype.getProjection); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getState', + ol.source.TileWMS.prototype.getState); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setAttributions', + ol.source.TileWMS.prototype.setAttributions); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'get', + ol.source.TileWMS.prototype.get); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getKeys', + ol.source.TileWMS.prototype.getKeys); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getProperties', + ol.source.TileWMS.prototype.getProperties); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'set', + ol.source.TileWMS.prototype.set); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'setProperties', + ol.source.TileWMS.prototype.setProperties); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'unset', + ol.source.TileWMS.prototype.unset); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'changed', + ol.source.TileWMS.prototype.changed); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'dispatchEvent', + ol.source.TileWMS.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'getRevision', + ol.source.TileWMS.prototype.getRevision); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'on', + ol.source.TileWMS.prototype.on); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'once', + ol.source.TileWMS.prototype.once); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'un', + ol.source.TileWMS.prototype.un); + +goog.exportProperty( + ol.source.TileWMS.prototype, + 'unByKey', + ol.source.TileWMS.prototype.unByKey); + +goog.exportProperty( + ol.source.VectorEvent.prototype, + 'type', + ol.source.VectorEvent.prototype.type); + +goog.exportProperty( + ol.source.VectorEvent.prototype, + 'target', + ol.source.VectorEvent.prototype.target); + +goog.exportProperty( + ol.source.VectorEvent.prototype, + 'preventDefault', + ol.source.VectorEvent.prototype.preventDefault); + +goog.exportProperty( + ol.source.VectorEvent.prototype, + 'stopPropagation', + ol.source.VectorEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getTileLoadFunction', + ol.source.VectorTile.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getTileUrlFunction', + ol.source.VectorTile.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getUrls', + ol.source.VectorTile.prototype.getUrls); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'setTileLoadFunction', + ol.source.VectorTile.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'setTileUrlFunction', + ol.source.VectorTile.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'setUrl', + ol.source.VectorTile.prototype.setUrl); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'setUrls', + ol.source.VectorTile.prototype.setUrls); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getTileGrid', + ol.source.VectorTile.prototype.getTileGrid); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'refresh', + ol.source.VectorTile.prototype.refresh); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getAttributions', + ol.source.VectorTile.prototype.getAttributions); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getLogo', + ol.source.VectorTile.prototype.getLogo); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getProjection', + ol.source.VectorTile.prototype.getProjection); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getState', + ol.source.VectorTile.prototype.getState); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'setAttributions', + ol.source.VectorTile.prototype.setAttributions); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'get', + ol.source.VectorTile.prototype.get); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getKeys', + ol.source.VectorTile.prototype.getKeys); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getProperties', + ol.source.VectorTile.prototype.getProperties); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'set', + ol.source.VectorTile.prototype.set); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'setProperties', + ol.source.VectorTile.prototype.setProperties); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'unset', + ol.source.VectorTile.prototype.unset); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'changed', + ol.source.VectorTile.prototype.changed); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'dispatchEvent', + ol.source.VectorTile.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'getRevision', + ol.source.VectorTile.prototype.getRevision); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'on', + ol.source.VectorTile.prototype.on); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'once', + ol.source.VectorTile.prototype.once); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'un', + ol.source.VectorTile.prototype.un); + +goog.exportProperty( + ol.source.VectorTile.prototype, + 'unByKey', + ol.source.VectorTile.prototype.unByKey); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setRenderReprojectionEdges', + ol.source.WMTS.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setTileGridForProjection', + ol.source.WMTS.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getTileLoadFunction', + ol.source.WMTS.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getTileUrlFunction', + ol.source.WMTS.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getUrls', + ol.source.WMTS.prototype.getUrls); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setTileLoadFunction', + ol.source.WMTS.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setTileUrlFunction', + ol.source.WMTS.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setUrl', + ol.source.WMTS.prototype.setUrl); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setUrls', + ol.source.WMTS.prototype.setUrls); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getTileGrid', + ol.source.WMTS.prototype.getTileGrid); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'refresh', + ol.source.WMTS.prototype.refresh); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getAttributions', + ol.source.WMTS.prototype.getAttributions); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getLogo', + ol.source.WMTS.prototype.getLogo); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getProjection', + ol.source.WMTS.prototype.getProjection); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getState', + ol.source.WMTS.prototype.getState); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setAttributions', + ol.source.WMTS.prototype.setAttributions); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'get', + ol.source.WMTS.prototype.get); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getKeys', + ol.source.WMTS.prototype.getKeys); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getProperties', + ol.source.WMTS.prototype.getProperties); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'set', + ol.source.WMTS.prototype.set); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'setProperties', + ol.source.WMTS.prototype.setProperties); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'unset', + ol.source.WMTS.prototype.unset); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'changed', + ol.source.WMTS.prototype.changed); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'dispatchEvent', + ol.source.WMTS.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'getRevision', + ol.source.WMTS.prototype.getRevision); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'on', + ol.source.WMTS.prototype.on); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'once', + ol.source.WMTS.prototype.once); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'un', + ol.source.WMTS.prototype.un); + +goog.exportProperty( + ol.source.WMTS.prototype, + 'unByKey', + ol.source.WMTS.prototype.unByKey); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setRenderReprojectionEdges', + ol.source.Zoomify.prototype.setRenderReprojectionEdges); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setTileGridForProjection', + ol.source.Zoomify.prototype.setTileGridForProjection); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getTileLoadFunction', + ol.source.Zoomify.prototype.getTileLoadFunction); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getTileUrlFunction', + ol.source.Zoomify.prototype.getTileUrlFunction); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getUrls', + ol.source.Zoomify.prototype.getUrls); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setTileLoadFunction', + ol.source.Zoomify.prototype.setTileLoadFunction); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setTileUrlFunction', + ol.source.Zoomify.prototype.setTileUrlFunction); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setUrl', + ol.source.Zoomify.prototype.setUrl); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setUrls', + ol.source.Zoomify.prototype.setUrls); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getTileGrid', + ol.source.Zoomify.prototype.getTileGrid); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'refresh', + ol.source.Zoomify.prototype.refresh); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getAttributions', + ol.source.Zoomify.prototype.getAttributions); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getLogo', + ol.source.Zoomify.prototype.getLogo); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getProjection', + ol.source.Zoomify.prototype.getProjection); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getState', + ol.source.Zoomify.prototype.getState); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setAttributions', + ol.source.Zoomify.prototype.setAttributions); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'get', + ol.source.Zoomify.prototype.get); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getKeys', + ol.source.Zoomify.prototype.getKeys); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getProperties', + ol.source.Zoomify.prototype.getProperties); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'set', + ol.source.Zoomify.prototype.set); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'setProperties', + ol.source.Zoomify.prototype.setProperties); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'unset', + ol.source.Zoomify.prototype.unset); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'changed', + ol.source.Zoomify.prototype.changed); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'dispatchEvent', + ol.source.Zoomify.prototype.dispatchEvent); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'getRevision', + ol.source.Zoomify.prototype.getRevision); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'on', + ol.source.Zoomify.prototype.on); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'once', + ol.source.Zoomify.prototype.once); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'un', + ol.source.Zoomify.prototype.un); + +goog.exportProperty( + ol.source.Zoomify.prototype, + 'unByKey', + ol.source.Zoomify.prototype.unByKey); + +goog.exportProperty( + ol.reproj.Tile.prototype, + 'getTileCoord', + ol.reproj.Tile.prototype.getTileCoord); + +goog.exportProperty( + ol.reproj.Tile.prototype, + 'load', + ol.reproj.Tile.prototype.load); + +goog.exportProperty( + ol.renderer.Layer.prototype, + 'changed', + ol.renderer.Layer.prototype.changed); + +goog.exportProperty( + ol.renderer.Layer.prototype, + 'dispatchEvent', + ol.renderer.Layer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.Layer.prototype, + 'getRevision', + ol.renderer.Layer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.Layer.prototype, + 'on', + ol.renderer.Layer.prototype.on); + +goog.exportProperty( + ol.renderer.Layer.prototype, + 'once', + ol.renderer.Layer.prototype.once); + +goog.exportProperty( + ol.renderer.Layer.prototype, + 'un', + ol.renderer.Layer.prototype.un); + +goog.exportProperty( + ol.renderer.Layer.prototype, + 'unByKey', + ol.renderer.Layer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.webgl.Layer.prototype, + 'changed', + ol.renderer.webgl.Layer.prototype.changed); + +goog.exportProperty( + ol.renderer.webgl.Layer.prototype, + 'dispatchEvent', + ol.renderer.webgl.Layer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.webgl.Layer.prototype, + 'getRevision', + ol.renderer.webgl.Layer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.webgl.Layer.prototype, + 'on', + ol.renderer.webgl.Layer.prototype.on); + +goog.exportProperty( + ol.renderer.webgl.Layer.prototype, + 'once', + ol.renderer.webgl.Layer.prototype.once); + +goog.exportProperty( + ol.renderer.webgl.Layer.prototype, + 'un', + ol.renderer.webgl.Layer.prototype.un); + +goog.exportProperty( + ol.renderer.webgl.Layer.prototype, + 'unByKey', + ol.renderer.webgl.Layer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.webgl.ImageLayer.prototype, + 'changed', + ol.renderer.webgl.ImageLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.webgl.ImageLayer.prototype, + 'dispatchEvent', + ol.renderer.webgl.ImageLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.webgl.ImageLayer.prototype, + 'getRevision', + ol.renderer.webgl.ImageLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.webgl.ImageLayer.prototype, + 'on', + ol.renderer.webgl.ImageLayer.prototype.on); + +goog.exportProperty( + ol.renderer.webgl.ImageLayer.prototype, + 'once', + ol.renderer.webgl.ImageLayer.prototype.once); + +goog.exportProperty( + ol.renderer.webgl.ImageLayer.prototype, + 'un', + ol.renderer.webgl.ImageLayer.prototype.un); + +goog.exportProperty( + ol.renderer.webgl.ImageLayer.prototype, + 'unByKey', + ol.renderer.webgl.ImageLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.webgl.TileLayer.prototype, + 'changed', + ol.renderer.webgl.TileLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.webgl.TileLayer.prototype, + 'dispatchEvent', + ol.renderer.webgl.TileLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.webgl.TileLayer.prototype, + 'getRevision', + ol.renderer.webgl.TileLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.webgl.TileLayer.prototype, + 'on', + ol.renderer.webgl.TileLayer.prototype.on); + +goog.exportProperty( + ol.renderer.webgl.TileLayer.prototype, + 'once', + ol.renderer.webgl.TileLayer.prototype.once); + +goog.exportProperty( + ol.renderer.webgl.TileLayer.prototype, + 'un', + ol.renderer.webgl.TileLayer.prototype.un); + +goog.exportProperty( + ol.renderer.webgl.TileLayer.prototype, + 'unByKey', + ol.renderer.webgl.TileLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.webgl.VectorLayer.prototype, + 'changed', + ol.renderer.webgl.VectorLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.webgl.VectorLayer.prototype, + 'dispatchEvent', + ol.renderer.webgl.VectorLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.webgl.VectorLayer.prototype, + 'getRevision', + ol.renderer.webgl.VectorLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.webgl.VectorLayer.prototype, + 'on', + ol.renderer.webgl.VectorLayer.prototype.on); + +goog.exportProperty( + ol.renderer.webgl.VectorLayer.prototype, + 'once', + ol.renderer.webgl.VectorLayer.prototype.once); + +goog.exportProperty( + ol.renderer.webgl.VectorLayer.prototype, + 'un', + ol.renderer.webgl.VectorLayer.prototype.un); + +goog.exportProperty( + ol.renderer.webgl.VectorLayer.prototype, + 'unByKey', + ol.renderer.webgl.VectorLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.dom.Layer.prototype, + 'changed', + ol.renderer.dom.Layer.prototype.changed); + +goog.exportProperty( + ol.renderer.dom.Layer.prototype, + 'dispatchEvent', + ol.renderer.dom.Layer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.dom.Layer.prototype, + 'getRevision', + ol.renderer.dom.Layer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.dom.Layer.prototype, + 'on', + ol.renderer.dom.Layer.prototype.on); + +goog.exportProperty( + ol.renderer.dom.Layer.prototype, + 'once', + ol.renderer.dom.Layer.prototype.once); + +goog.exportProperty( + ol.renderer.dom.Layer.prototype, + 'un', + ol.renderer.dom.Layer.prototype.un); + +goog.exportProperty( + ol.renderer.dom.Layer.prototype, + 'unByKey', + ol.renderer.dom.Layer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.dom.ImageLayer.prototype, + 'changed', + ol.renderer.dom.ImageLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.dom.ImageLayer.prototype, + 'dispatchEvent', + ol.renderer.dom.ImageLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.dom.ImageLayer.prototype, + 'getRevision', + ol.renderer.dom.ImageLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.dom.ImageLayer.prototype, + 'on', + ol.renderer.dom.ImageLayer.prototype.on); + +goog.exportProperty( + ol.renderer.dom.ImageLayer.prototype, + 'once', + ol.renderer.dom.ImageLayer.prototype.once); + +goog.exportProperty( + ol.renderer.dom.ImageLayer.prototype, + 'un', + ol.renderer.dom.ImageLayer.prototype.un); + +goog.exportProperty( + ol.renderer.dom.ImageLayer.prototype, + 'unByKey', + ol.renderer.dom.ImageLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.dom.TileLayer.prototype, + 'changed', + ol.renderer.dom.TileLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.dom.TileLayer.prototype, + 'dispatchEvent', + ol.renderer.dom.TileLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.dom.TileLayer.prototype, + 'getRevision', + ol.renderer.dom.TileLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.dom.TileLayer.prototype, + 'on', + ol.renderer.dom.TileLayer.prototype.on); + +goog.exportProperty( + ol.renderer.dom.TileLayer.prototype, + 'once', + ol.renderer.dom.TileLayer.prototype.once); + +goog.exportProperty( + ol.renderer.dom.TileLayer.prototype, + 'un', + ol.renderer.dom.TileLayer.prototype.un); + +goog.exportProperty( + ol.renderer.dom.TileLayer.prototype, + 'unByKey', + ol.renderer.dom.TileLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.dom.VectorLayer.prototype, + 'changed', + ol.renderer.dom.VectorLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.dom.VectorLayer.prototype, + 'dispatchEvent', + ol.renderer.dom.VectorLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.dom.VectorLayer.prototype, + 'getRevision', + ol.renderer.dom.VectorLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.dom.VectorLayer.prototype, + 'on', + ol.renderer.dom.VectorLayer.prototype.on); + +goog.exportProperty( + ol.renderer.dom.VectorLayer.prototype, + 'once', + ol.renderer.dom.VectorLayer.prototype.once); + +goog.exportProperty( + ol.renderer.dom.VectorLayer.prototype, + 'un', + ol.renderer.dom.VectorLayer.prototype.un); + +goog.exportProperty( + ol.renderer.dom.VectorLayer.prototype, + 'unByKey', + ol.renderer.dom.VectorLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.canvas.Layer.prototype, + 'changed', + ol.renderer.canvas.Layer.prototype.changed); + +goog.exportProperty( + ol.renderer.canvas.Layer.prototype, + 'dispatchEvent', + ol.renderer.canvas.Layer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.canvas.Layer.prototype, + 'getRevision', + ol.renderer.canvas.Layer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.canvas.Layer.prototype, + 'on', + ol.renderer.canvas.Layer.prototype.on); + +goog.exportProperty( + ol.renderer.canvas.Layer.prototype, + 'once', + ol.renderer.canvas.Layer.prototype.once); + +goog.exportProperty( + ol.renderer.canvas.Layer.prototype, + 'un', + ol.renderer.canvas.Layer.prototype.un); + +goog.exportProperty( + ol.renderer.canvas.Layer.prototype, + 'unByKey', + ol.renderer.canvas.Layer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.canvas.ImageLayer.prototype, + 'changed', + ol.renderer.canvas.ImageLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.canvas.ImageLayer.prototype, + 'dispatchEvent', + ol.renderer.canvas.ImageLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.canvas.ImageLayer.prototype, + 'getRevision', + ol.renderer.canvas.ImageLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.canvas.ImageLayer.prototype, + 'on', + ol.renderer.canvas.ImageLayer.prototype.on); + +goog.exportProperty( + ol.renderer.canvas.ImageLayer.prototype, + 'once', + ol.renderer.canvas.ImageLayer.prototype.once); + +goog.exportProperty( + ol.renderer.canvas.ImageLayer.prototype, + 'un', + ol.renderer.canvas.ImageLayer.prototype.un); + +goog.exportProperty( + ol.renderer.canvas.ImageLayer.prototype, + 'unByKey', + ol.renderer.canvas.ImageLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.canvas.TileLayer.prototype, + 'changed', + ol.renderer.canvas.TileLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.canvas.TileLayer.prototype, + 'dispatchEvent', + ol.renderer.canvas.TileLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.canvas.TileLayer.prototype, + 'getRevision', + ol.renderer.canvas.TileLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.canvas.TileLayer.prototype, + 'on', + ol.renderer.canvas.TileLayer.prototype.on); + +goog.exportProperty( + ol.renderer.canvas.TileLayer.prototype, + 'once', + ol.renderer.canvas.TileLayer.prototype.once); + +goog.exportProperty( + ol.renderer.canvas.TileLayer.prototype, + 'un', + ol.renderer.canvas.TileLayer.prototype.un); + +goog.exportProperty( + ol.renderer.canvas.TileLayer.prototype, + 'unByKey', + ol.renderer.canvas.TileLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.canvas.VectorLayer.prototype, + 'changed', + ol.renderer.canvas.VectorLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.canvas.VectorLayer.prototype, + 'dispatchEvent', + ol.renderer.canvas.VectorLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.canvas.VectorLayer.prototype, + 'getRevision', + ol.renderer.canvas.VectorLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.canvas.VectorLayer.prototype, + 'on', + ol.renderer.canvas.VectorLayer.prototype.on); + +goog.exportProperty( + ol.renderer.canvas.VectorLayer.prototype, + 'once', + ol.renderer.canvas.VectorLayer.prototype.once); + +goog.exportProperty( + ol.renderer.canvas.VectorLayer.prototype, + 'un', + ol.renderer.canvas.VectorLayer.prototype.un); + +goog.exportProperty( + ol.renderer.canvas.VectorLayer.prototype, + 'unByKey', + ol.renderer.canvas.VectorLayer.prototype.unByKey); + +goog.exportProperty( + ol.renderer.canvas.VectorTileLayer.prototype, + 'changed', + ol.renderer.canvas.VectorTileLayer.prototype.changed); + +goog.exportProperty( + ol.renderer.canvas.VectorTileLayer.prototype, + 'dispatchEvent', + ol.renderer.canvas.VectorTileLayer.prototype.dispatchEvent); + +goog.exportProperty( + ol.renderer.canvas.VectorTileLayer.prototype, + 'getRevision', + ol.renderer.canvas.VectorTileLayer.prototype.getRevision); + +goog.exportProperty( + ol.renderer.canvas.VectorTileLayer.prototype, + 'on', + ol.renderer.canvas.VectorTileLayer.prototype.on); + +goog.exportProperty( + ol.renderer.canvas.VectorTileLayer.prototype, + 'once', + ol.renderer.canvas.VectorTileLayer.prototype.once); + +goog.exportProperty( + ol.renderer.canvas.VectorTileLayer.prototype, + 'un', + ol.renderer.canvas.VectorTileLayer.prototype.un); + +goog.exportProperty( + ol.renderer.canvas.VectorTileLayer.prototype, + 'unByKey', + ol.renderer.canvas.VectorTileLayer.prototype.unByKey); + +goog.exportProperty( + ol.render.Event.prototype, + 'type', + ol.render.Event.prototype.type); + +goog.exportProperty( + ol.render.Event.prototype, + 'target', + ol.render.Event.prototype.target); + +goog.exportProperty( + ol.render.Event.prototype, + 'preventDefault', + ol.render.Event.prototype.preventDefault); + +goog.exportProperty( + ol.render.Event.prototype, + 'stopPropagation', + ol.render.Event.prototype.stopPropagation); + +goog.exportProperty( + ol.pointer.PointerEvent.prototype, + 'type', + ol.pointer.PointerEvent.prototype.type); + +goog.exportProperty( + ol.pointer.PointerEvent.prototype, + 'target', + ol.pointer.PointerEvent.prototype.target); + +goog.exportProperty( + ol.pointer.PointerEvent.prototype, + 'preventDefault', + ol.pointer.PointerEvent.prototype.preventDefault); + +goog.exportProperty( + ol.pointer.PointerEvent.prototype, + 'stopPropagation', + ol.pointer.PointerEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.layer.Base.prototype, + 'get', + ol.layer.Base.prototype.get); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getKeys', + ol.layer.Base.prototype.getKeys); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getProperties', + ol.layer.Base.prototype.getProperties); + +goog.exportProperty( + ol.layer.Base.prototype, + 'set', + ol.layer.Base.prototype.set); + +goog.exportProperty( + ol.layer.Base.prototype, + 'setProperties', + ol.layer.Base.prototype.setProperties); + +goog.exportProperty( + ol.layer.Base.prototype, + 'unset', + ol.layer.Base.prototype.unset); + +goog.exportProperty( + ol.layer.Base.prototype, + 'changed', + ol.layer.Base.prototype.changed); + +goog.exportProperty( + ol.layer.Base.prototype, + 'dispatchEvent', + ol.layer.Base.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.Base.prototype, + 'getRevision', + ol.layer.Base.prototype.getRevision); + +goog.exportProperty( + ol.layer.Base.prototype, + 'on', + ol.layer.Base.prototype.on); + +goog.exportProperty( + ol.layer.Base.prototype, + 'once', + ol.layer.Base.prototype.once); + +goog.exportProperty( + ol.layer.Base.prototype, + 'un', + ol.layer.Base.prototype.un); + +goog.exportProperty( + ol.layer.Base.prototype, + 'unByKey', + ol.layer.Base.prototype.unByKey); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getExtent', + ol.layer.Layer.prototype.getExtent); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getMaxResolution', + ol.layer.Layer.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getMinResolution', + ol.layer.Layer.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getOpacity', + ol.layer.Layer.prototype.getOpacity); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getVisible', + ol.layer.Layer.prototype.getVisible); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getZIndex', + ol.layer.Layer.prototype.getZIndex); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setExtent', + ol.layer.Layer.prototype.setExtent); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setMaxResolution', + ol.layer.Layer.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setMinResolution', + ol.layer.Layer.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setOpacity', + ol.layer.Layer.prototype.setOpacity); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setVisible', + ol.layer.Layer.prototype.setVisible); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setZIndex', + ol.layer.Layer.prototype.setZIndex); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'get', + ol.layer.Layer.prototype.get); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getKeys', + ol.layer.Layer.prototype.getKeys); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getProperties', + ol.layer.Layer.prototype.getProperties); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'set', + ol.layer.Layer.prototype.set); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'setProperties', + ol.layer.Layer.prototype.setProperties); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'unset', + ol.layer.Layer.prototype.unset); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'changed', + ol.layer.Layer.prototype.changed); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'dispatchEvent', + ol.layer.Layer.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'getRevision', + ol.layer.Layer.prototype.getRevision); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'on', + ol.layer.Layer.prototype.on); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'once', + ol.layer.Layer.prototype.once); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'un', + ol.layer.Layer.prototype.un); + +goog.exportProperty( + ol.layer.Layer.prototype, + 'unByKey', + ol.layer.Layer.prototype.unByKey); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setMap', + ol.layer.Vector.prototype.setMap); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setSource', + ol.layer.Vector.prototype.setSource); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getExtent', + ol.layer.Vector.prototype.getExtent); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getMaxResolution', + ol.layer.Vector.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getMinResolution', + ol.layer.Vector.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getOpacity', + ol.layer.Vector.prototype.getOpacity); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getVisible', + ol.layer.Vector.prototype.getVisible); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getZIndex', + ol.layer.Vector.prototype.getZIndex); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setExtent', + ol.layer.Vector.prototype.setExtent); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setMaxResolution', + ol.layer.Vector.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setMinResolution', + ol.layer.Vector.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setOpacity', + ol.layer.Vector.prototype.setOpacity); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setVisible', + ol.layer.Vector.prototype.setVisible); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setZIndex', + ol.layer.Vector.prototype.setZIndex); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'get', + ol.layer.Vector.prototype.get); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getKeys', + ol.layer.Vector.prototype.getKeys); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getProperties', + ol.layer.Vector.prototype.getProperties); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'set', + ol.layer.Vector.prototype.set); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'setProperties', + ol.layer.Vector.prototype.setProperties); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'unset', + ol.layer.Vector.prototype.unset); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'changed', + ol.layer.Vector.prototype.changed); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'dispatchEvent', + ol.layer.Vector.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'getRevision', + ol.layer.Vector.prototype.getRevision); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'on', + ol.layer.Vector.prototype.on); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'once', + ol.layer.Vector.prototype.once); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'un', + ol.layer.Vector.prototype.un); + +goog.exportProperty( + ol.layer.Vector.prototype, + 'unByKey', + ol.layer.Vector.prototype.unByKey); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getSource', + ol.layer.Heatmap.prototype.getSource); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getStyle', + ol.layer.Heatmap.prototype.getStyle); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getStyleFunction', + ol.layer.Heatmap.prototype.getStyleFunction); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setStyle', + ol.layer.Heatmap.prototype.setStyle); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setMap', + ol.layer.Heatmap.prototype.setMap); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setSource', + ol.layer.Heatmap.prototype.setSource); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getExtent', + ol.layer.Heatmap.prototype.getExtent); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getMaxResolution', + ol.layer.Heatmap.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getMinResolution', + ol.layer.Heatmap.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getOpacity', + ol.layer.Heatmap.prototype.getOpacity); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getVisible', + ol.layer.Heatmap.prototype.getVisible); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getZIndex', + ol.layer.Heatmap.prototype.getZIndex); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setExtent', + ol.layer.Heatmap.prototype.setExtent); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setMaxResolution', + ol.layer.Heatmap.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setMinResolution', + ol.layer.Heatmap.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setOpacity', + ol.layer.Heatmap.prototype.setOpacity); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setVisible', + ol.layer.Heatmap.prototype.setVisible); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setZIndex', + ol.layer.Heatmap.prototype.setZIndex); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'get', + ol.layer.Heatmap.prototype.get); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getKeys', + ol.layer.Heatmap.prototype.getKeys); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getProperties', + ol.layer.Heatmap.prototype.getProperties); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'set', + ol.layer.Heatmap.prototype.set); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'setProperties', + ol.layer.Heatmap.prototype.setProperties); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'unset', + ol.layer.Heatmap.prototype.unset); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'changed', + ol.layer.Heatmap.prototype.changed); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'dispatchEvent', + ol.layer.Heatmap.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'getRevision', + ol.layer.Heatmap.prototype.getRevision); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'on', + ol.layer.Heatmap.prototype.on); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'once', + ol.layer.Heatmap.prototype.once); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'un', + ol.layer.Heatmap.prototype.un); + +goog.exportProperty( + ol.layer.Heatmap.prototype, + 'unByKey', + ol.layer.Heatmap.prototype.unByKey); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setMap', + ol.layer.Image.prototype.setMap); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setSource', + ol.layer.Image.prototype.setSource); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getExtent', + ol.layer.Image.prototype.getExtent); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getMaxResolution', + ol.layer.Image.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getMinResolution', + ol.layer.Image.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getOpacity', + ol.layer.Image.prototype.getOpacity); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getVisible', + ol.layer.Image.prototype.getVisible); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getZIndex', + ol.layer.Image.prototype.getZIndex); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setExtent', + ol.layer.Image.prototype.setExtent); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setMaxResolution', + ol.layer.Image.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setMinResolution', + ol.layer.Image.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setOpacity', + ol.layer.Image.prototype.setOpacity); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setVisible', + ol.layer.Image.prototype.setVisible); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setZIndex', + ol.layer.Image.prototype.setZIndex); + +goog.exportProperty( + ol.layer.Image.prototype, + 'get', + ol.layer.Image.prototype.get); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getKeys', + ol.layer.Image.prototype.getKeys); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getProperties', + ol.layer.Image.prototype.getProperties); + +goog.exportProperty( + ol.layer.Image.prototype, + 'set', + ol.layer.Image.prototype.set); + +goog.exportProperty( + ol.layer.Image.prototype, + 'setProperties', + ol.layer.Image.prototype.setProperties); + +goog.exportProperty( + ol.layer.Image.prototype, + 'unset', + ol.layer.Image.prototype.unset); + +goog.exportProperty( + ol.layer.Image.prototype, + 'changed', + ol.layer.Image.prototype.changed); + +goog.exportProperty( + ol.layer.Image.prototype, + 'dispatchEvent', + ol.layer.Image.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.Image.prototype, + 'getRevision', + ol.layer.Image.prototype.getRevision); + +goog.exportProperty( + ol.layer.Image.prototype, + 'on', + ol.layer.Image.prototype.on); + +goog.exportProperty( + ol.layer.Image.prototype, + 'once', + ol.layer.Image.prototype.once); + +goog.exportProperty( + ol.layer.Image.prototype, + 'un', + ol.layer.Image.prototype.un); + +goog.exportProperty( + ol.layer.Image.prototype, + 'unByKey', + ol.layer.Image.prototype.unByKey); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getExtent', + ol.layer.Group.prototype.getExtent); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getMaxResolution', + ol.layer.Group.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getMinResolution', + ol.layer.Group.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getOpacity', + ol.layer.Group.prototype.getOpacity); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getVisible', + ol.layer.Group.prototype.getVisible); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getZIndex', + ol.layer.Group.prototype.getZIndex); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setExtent', + ol.layer.Group.prototype.setExtent); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setMaxResolution', + ol.layer.Group.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setMinResolution', + ol.layer.Group.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setOpacity', + ol.layer.Group.prototype.setOpacity); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setVisible', + ol.layer.Group.prototype.setVisible); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setZIndex', + ol.layer.Group.prototype.setZIndex); + +goog.exportProperty( + ol.layer.Group.prototype, + 'get', + ol.layer.Group.prototype.get); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getKeys', + ol.layer.Group.prototype.getKeys); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getProperties', + ol.layer.Group.prototype.getProperties); + +goog.exportProperty( + ol.layer.Group.prototype, + 'set', + ol.layer.Group.prototype.set); + +goog.exportProperty( + ol.layer.Group.prototype, + 'setProperties', + ol.layer.Group.prototype.setProperties); + +goog.exportProperty( + ol.layer.Group.prototype, + 'unset', + ol.layer.Group.prototype.unset); + +goog.exportProperty( + ol.layer.Group.prototype, + 'changed', + ol.layer.Group.prototype.changed); + +goog.exportProperty( + ol.layer.Group.prototype, + 'dispatchEvent', + ol.layer.Group.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.Group.prototype, + 'getRevision', + ol.layer.Group.prototype.getRevision); + +goog.exportProperty( + ol.layer.Group.prototype, + 'on', + ol.layer.Group.prototype.on); + +goog.exportProperty( + ol.layer.Group.prototype, + 'once', + ol.layer.Group.prototype.once); + +goog.exportProperty( + ol.layer.Group.prototype, + 'un', + ol.layer.Group.prototype.un); + +goog.exportProperty( + ol.layer.Group.prototype, + 'unByKey', + ol.layer.Group.prototype.unByKey); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setMap', + ol.layer.Tile.prototype.setMap); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setSource', + ol.layer.Tile.prototype.setSource); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getExtent', + ol.layer.Tile.prototype.getExtent); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getMaxResolution', + ol.layer.Tile.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getMinResolution', + ol.layer.Tile.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getOpacity', + ol.layer.Tile.prototype.getOpacity); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getVisible', + ol.layer.Tile.prototype.getVisible); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getZIndex', + ol.layer.Tile.prototype.getZIndex); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setExtent', + ol.layer.Tile.prototype.setExtent); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setMaxResolution', + ol.layer.Tile.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setMinResolution', + ol.layer.Tile.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setOpacity', + ol.layer.Tile.prototype.setOpacity); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setVisible', + ol.layer.Tile.prototype.setVisible); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setZIndex', + ol.layer.Tile.prototype.setZIndex); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'get', + ol.layer.Tile.prototype.get); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getKeys', + ol.layer.Tile.prototype.getKeys); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getProperties', + ol.layer.Tile.prototype.getProperties); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'set', + ol.layer.Tile.prototype.set); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'setProperties', + ol.layer.Tile.prototype.setProperties); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'unset', + ol.layer.Tile.prototype.unset); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'changed', + ol.layer.Tile.prototype.changed); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'dispatchEvent', + ol.layer.Tile.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'getRevision', + ol.layer.Tile.prototype.getRevision); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'on', + ol.layer.Tile.prototype.on); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'once', + ol.layer.Tile.prototype.once); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'un', + ol.layer.Tile.prototype.un); + +goog.exportProperty( + ol.layer.Tile.prototype, + 'unByKey', + ol.layer.Tile.prototype.unByKey); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getSource', + ol.layer.VectorTile.prototype.getSource); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getStyle', + ol.layer.VectorTile.prototype.getStyle); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getStyleFunction', + ol.layer.VectorTile.prototype.getStyleFunction); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setStyle', + ol.layer.VectorTile.prototype.setStyle); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setMap', + ol.layer.VectorTile.prototype.setMap); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setSource', + ol.layer.VectorTile.prototype.setSource); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getExtent', + ol.layer.VectorTile.prototype.getExtent); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getMaxResolution', + ol.layer.VectorTile.prototype.getMaxResolution); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getMinResolution', + ol.layer.VectorTile.prototype.getMinResolution); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getOpacity', + ol.layer.VectorTile.prototype.getOpacity); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getVisible', + ol.layer.VectorTile.prototype.getVisible); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getZIndex', + ol.layer.VectorTile.prototype.getZIndex); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setExtent', + ol.layer.VectorTile.prototype.setExtent); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setMaxResolution', + ol.layer.VectorTile.prototype.setMaxResolution); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setMinResolution', + ol.layer.VectorTile.prototype.setMinResolution); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setOpacity', + ol.layer.VectorTile.prototype.setOpacity); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setVisible', + ol.layer.VectorTile.prototype.setVisible); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setZIndex', + ol.layer.VectorTile.prototype.setZIndex); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'get', + ol.layer.VectorTile.prototype.get); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getKeys', + ol.layer.VectorTile.prototype.getKeys); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getProperties', + ol.layer.VectorTile.prototype.getProperties); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'set', + ol.layer.VectorTile.prototype.set); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'setProperties', + ol.layer.VectorTile.prototype.setProperties); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'unset', + ol.layer.VectorTile.prototype.unset); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'changed', + ol.layer.VectorTile.prototype.changed); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'dispatchEvent', + ol.layer.VectorTile.prototype.dispatchEvent); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'getRevision', + ol.layer.VectorTile.prototype.getRevision); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'on', + ol.layer.VectorTile.prototype.on); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'once', + ol.layer.VectorTile.prototype.once); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'un', + ol.layer.VectorTile.prototype.un); + +goog.exportProperty( + ol.layer.VectorTile.prototype, + 'unByKey', + ol.layer.VectorTile.prototype.unByKey); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'get', + ol.interaction.Interaction.prototype.get); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'getKeys', + ol.interaction.Interaction.prototype.getKeys); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'getProperties', + ol.interaction.Interaction.prototype.getProperties); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'set', + ol.interaction.Interaction.prototype.set); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'setProperties', + ol.interaction.Interaction.prototype.setProperties); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'unset', + ol.interaction.Interaction.prototype.unset); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'changed', + ol.interaction.Interaction.prototype.changed); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'dispatchEvent', + ol.interaction.Interaction.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'getRevision', + ol.interaction.Interaction.prototype.getRevision); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'on', + ol.interaction.Interaction.prototype.on); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'once', + ol.interaction.Interaction.prototype.once); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'un', + ol.interaction.Interaction.prototype.un); + +goog.exportProperty( + ol.interaction.Interaction.prototype, + 'unByKey', + ol.interaction.Interaction.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'getActive', + ol.interaction.DoubleClickZoom.prototype.getActive); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'getMap', + ol.interaction.DoubleClickZoom.prototype.getMap); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'setActive', + ol.interaction.DoubleClickZoom.prototype.setActive); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'get', + ol.interaction.DoubleClickZoom.prototype.get); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'getKeys', + ol.interaction.DoubleClickZoom.prototype.getKeys); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'getProperties', + ol.interaction.DoubleClickZoom.prototype.getProperties); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'set', + ol.interaction.DoubleClickZoom.prototype.set); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'setProperties', + ol.interaction.DoubleClickZoom.prototype.setProperties); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'unset', + ol.interaction.DoubleClickZoom.prototype.unset); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'changed', + ol.interaction.DoubleClickZoom.prototype.changed); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'dispatchEvent', + ol.interaction.DoubleClickZoom.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'getRevision', + ol.interaction.DoubleClickZoom.prototype.getRevision); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'on', + ol.interaction.DoubleClickZoom.prototype.on); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'once', + ol.interaction.DoubleClickZoom.prototype.once); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'un', + ol.interaction.DoubleClickZoom.prototype.un); + +goog.exportProperty( + ol.interaction.DoubleClickZoom.prototype, + 'unByKey', + ol.interaction.DoubleClickZoom.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'getActive', + ol.interaction.DragAndDrop.prototype.getActive); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'getMap', + ol.interaction.DragAndDrop.prototype.getMap); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'setActive', + ol.interaction.DragAndDrop.prototype.setActive); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'get', + ol.interaction.DragAndDrop.prototype.get); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'getKeys', + ol.interaction.DragAndDrop.prototype.getKeys); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'getProperties', + ol.interaction.DragAndDrop.prototype.getProperties); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'set', + ol.interaction.DragAndDrop.prototype.set); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'setProperties', + ol.interaction.DragAndDrop.prototype.setProperties); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'unset', + ol.interaction.DragAndDrop.prototype.unset); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'changed', + ol.interaction.DragAndDrop.prototype.changed); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'dispatchEvent', + ol.interaction.DragAndDrop.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'getRevision', + ol.interaction.DragAndDrop.prototype.getRevision); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'on', + ol.interaction.DragAndDrop.prototype.on); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'once', + ol.interaction.DragAndDrop.prototype.once); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'un', + ol.interaction.DragAndDrop.prototype.un); + +goog.exportProperty( + ol.interaction.DragAndDrop.prototype, + 'unByKey', + ol.interaction.DragAndDrop.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DragAndDropEvent.prototype, + 'type', + ol.interaction.DragAndDropEvent.prototype.type); + +goog.exportProperty( + ol.interaction.DragAndDropEvent.prototype, + 'target', + ol.interaction.DragAndDropEvent.prototype.target); + +goog.exportProperty( + ol.interaction.DragAndDropEvent.prototype, + 'preventDefault', + ol.interaction.DragAndDropEvent.prototype.preventDefault); + +goog.exportProperty( + ol.interaction.DragAndDropEvent.prototype, + 'stopPropagation', + ol.interaction.DragAndDropEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.DragBoxEvent.prototype, + 'type', + ol.DragBoxEvent.prototype.type); + +goog.exportProperty( + ol.DragBoxEvent.prototype, + 'target', + ol.DragBoxEvent.prototype.target); + +goog.exportProperty( + ol.DragBoxEvent.prototype, + 'preventDefault', + ol.DragBoxEvent.prototype.preventDefault); + +goog.exportProperty( + ol.DragBoxEvent.prototype, + 'stopPropagation', + ol.DragBoxEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'getActive', + ol.interaction.Pointer.prototype.getActive); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'getMap', + ol.interaction.Pointer.prototype.getMap); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'setActive', + ol.interaction.Pointer.prototype.setActive); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'get', + ol.interaction.Pointer.prototype.get); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'getKeys', + ol.interaction.Pointer.prototype.getKeys); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'getProperties', + ol.interaction.Pointer.prototype.getProperties); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'set', + ol.interaction.Pointer.prototype.set); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'setProperties', + ol.interaction.Pointer.prototype.setProperties); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'unset', + ol.interaction.Pointer.prototype.unset); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'changed', + ol.interaction.Pointer.prototype.changed); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'dispatchEvent', + ol.interaction.Pointer.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'getRevision', + ol.interaction.Pointer.prototype.getRevision); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'on', + ol.interaction.Pointer.prototype.on); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'once', + ol.interaction.Pointer.prototype.once); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'un', + ol.interaction.Pointer.prototype.un); + +goog.exportProperty( + ol.interaction.Pointer.prototype, + 'unByKey', + ol.interaction.Pointer.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'getActive', + ol.interaction.DragBox.prototype.getActive); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'getMap', + ol.interaction.DragBox.prototype.getMap); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'setActive', + ol.interaction.DragBox.prototype.setActive); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'get', + ol.interaction.DragBox.prototype.get); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'getKeys', + ol.interaction.DragBox.prototype.getKeys); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'getProperties', + ol.interaction.DragBox.prototype.getProperties); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'set', + ol.interaction.DragBox.prototype.set); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'setProperties', + ol.interaction.DragBox.prototype.setProperties); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'unset', + ol.interaction.DragBox.prototype.unset); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'changed', + ol.interaction.DragBox.prototype.changed); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'dispatchEvent', + ol.interaction.DragBox.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'getRevision', + ol.interaction.DragBox.prototype.getRevision); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'on', + ol.interaction.DragBox.prototype.on); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'once', + ol.interaction.DragBox.prototype.once); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'un', + ol.interaction.DragBox.prototype.un); + +goog.exportProperty( + ol.interaction.DragBox.prototype, + 'unByKey', + ol.interaction.DragBox.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'getActive', + ol.interaction.DragPan.prototype.getActive); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'getMap', + ol.interaction.DragPan.prototype.getMap); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'setActive', + ol.interaction.DragPan.prototype.setActive); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'get', + ol.interaction.DragPan.prototype.get); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'getKeys', + ol.interaction.DragPan.prototype.getKeys); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'getProperties', + ol.interaction.DragPan.prototype.getProperties); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'set', + ol.interaction.DragPan.prototype.set); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'setProperties', + ol.interaction.DragPan.prototype.setProperties); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'unset', + ol.interaction.DragPan.prototype.unset); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'changed', + ol.interaction.DragPan.prototype.changed); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'dispatchEvent', + ol.interaction.DragPan.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'getRevision', + ol.interaction.DragPan.prototype.getRevision); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'on', + ol.interaction.DragPan.prototype.on); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'once', + ol.interaction.DragPan.prototype.once); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'un', + ol.interaction.DragPan.prototype.un); + +goog.exportProperty( + ol.interaction.DragPan.prototype, + 'unByKey', + ol.interaction.DragPan.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'getActive', + ol.interaction.DragRotateAndZoom.prototype.getActive); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'getMap', + ol.interaction.DragRotateAndZoom.prototype.getMap); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'setActive', + ol.interaction.DragRotateAndZoom.prototype.setActive); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'get', + ol.interaction.DragRotateAndZoom.prototype.get); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'getKeys', + ol.interaction.DragRotateAndZoom.prototype.getKeys); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'getProperties', + ol.interaction.DragRotateAndZoom.prototype.getProperties); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'set', + ol.interaction.DragRotateAndZoom.prototype.set); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'setProperties', + ol.interaction.DragRotateAndZoom.prototype.setProperties); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'unset', + ol.interaction.DragRotateAndZoom.prototype.unset); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'changed', + ol.interaction.DragRotateAndZoom.prototype.changed); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'dispatchEvent', + ol.interaction.DragRotateAndZoom.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'getRevision', + ol.interaction.DragRotateAndZoom.prototype.getRevision); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'on', + ol.interaction.DragRotateAndZoom.prototype.on); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'once', + ol.interaction.DragRotateAndZoom.prototype.once); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'un', + ol.interaction.DragRotateAndZoom.prototype.un); + +goog.exportProperty( + ol.interaction.DragRotateAndZoom.prototype, + 'unByKey', + ol.interaction.DragRotateAndZoom.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'getActive', + ol.interaction.DragRotate.prototype.getActive); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'getMap', + ol.interaction.DragRotate.prototype.getMap); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'setActive', + ol.interaction.DragRotate.prototype.setActive); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'get', + ol.interaction.DragRotate.prototype.get); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'getKeys', + ol.interaction.DragRotate.prototype.getKeys); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'getProperties', + ol.interaction.DragRotate.prototype.getProperties); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'set', + ol.interaction.DragRotate.prototype.set); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'setProperties', + ol.interaction.DragRotate.prototype.setProperties); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'unset', + ol.interaction.DragRotate.prototype.unset); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'changed', + ol.interaction.DragRotate.prototype.changed); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'dispatchEvent', + ol.interaction.DragRotate.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'getRevision', + ol.interaction.DragRotate.prototype.getRevision); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'on', + ol.interaction.DragRotate.prototype.on); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'once', + ol.interaction.DragRotate.prototype.once); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'un', + ol.interaction.DragRotate.prototype.un); + +goog.exportProperty( + ol.interaction.DragRotate.prototype, + 'unByKey', + ol.interaction.DragRotate.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'getGeometry', + ol.interaction.DragZoom.prototype.getGeometry); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'getActive', + ol.interaction.DragZoom.prototype.getActive); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'getMap', + ol.interaction.DragZoom.prototype.getMap); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'setActive', + ol.interaction.DragZoom.prototype.setActive); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'get', + ol.interaction.DragZoom.prototype.get); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'getKeys', + ol.interaction.DragZoom.prototype.getKeys); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'getProperties', + ol.interaction.DragZoom.prototype.getProperties); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'set', + ol.interaction.DragZoom.prototype.set); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'setProperties', + ol.interaction.DragZoom.prototype.setProperties); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'unset', + ol.interaction.DragZoom.prototype.unset); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'changed', + ol.interaction.DragZoom.prototype.changed); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'dispatchEvent', + ol.interaction.DragZoom.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'getRevision', + ol.interaction.DragZoom.prototype.getRevision); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'on', + ol.interaction.DragZoom.prototype.on); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'once', + ol.interaction.DragZoom.prototype.once); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'un', + ol.interaction.DragZoom.prototype.un); + +goog.exportProperty( + ol.interaction.DragZoom.prototype, + 'unByKey', + ol.interaction.DragZoom.prototype.unByKey); + +goog.exportProperty( + ol.interaction.DrawEvent.prototype, + 'type', + ol.interaction.DrawEvent.prototype.type); + +goog.exportProperty( + ol.interaction.DrawEvent.prototype, + 'target', + ol.interaction.DrawEvent.prototype.target); + +goog.exportProperty( + ol.interaction.DrawEvent.prototype, + 'preventDefault', + ol.interaction.DrawEvent.prototype.preventDefault); + +goog.exportProperty( + ol.interaction.DrawEvent.prototype, + 'stopPropagation', + ol.interaction.DrawEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'getActive', + ol.interaction.Draw.prototype.getActive); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'getMap', + ol.interaction.Draw.prototype.getMap); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'setActive', + ol.interaction.Draw.prototype.setActive); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'get', + ol.interaction.Draw.prototype.get); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'getKeys', + ol.interaction.Draw.prototype.getKeys); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'getProperties', + ol.interaction.Draw.prototype.getProperties); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'set', + ol.interaction.Draw.prototype.set); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'setProperties', + ol.interaction.Draw.prototype.setProperties); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'unset', + ol.interaction.Draw.prototype.unset); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'changed', + ol.interaction.Draw.prototype.changed); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'dispatchEvent', + ol.interaction.Draw.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'getRevision', + ol.interaction.Draw.prototype.getRevision); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'on', + ol.interaction.Draw.prototype.on); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'once', + ol.interaction.Draw.prototype.once); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'un', + ol.interaction.Draw.prototype.un); + +goog.exportProperty( + ol.interaction.Draw.prototype, + 'unByKey', + ol.interaction.Draw.prototype.unByKey); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'getActive', + ol.interaction.KeyboardPan.prototype.getActive); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'getMap', + ol.interaction.KeyboardPan.prototype.getMap); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'setActive', + ol.interaction.KeyboardPan.prototype.setActive); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'get', + ol.interaction.KeyboardPan.prototype.get); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'getKeys', + ol.interaction.KeyboardPan.prototype.getKeys); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'getProperties', + ol.interaction.KeyboardPan.prototype.getProperties); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'set', + ol.interaction.KeyboardPan.prototype.set); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'setProperties', + ol.interaction.KeyboardPan.prototype.setProperties); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'unset', + ol.interaction.KeyboardPan.prototype.unset); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'changed', + ol.interaction.KeyboardPan.prototype.changed); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'dispatchEvent', + ol.interaction.KeyboardPan.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'getRevision', + ol.interaction.KeyboardPan.prototype.getRevision); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'on', + ol.interaction.KeyboardPan.prototype.on); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'once', + ol.interaction.KeyboardPan.prototype.once); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'un', + ol.interaction.KeyboardPan.prototype.un); + +goog.exportProperty( + ol.interaction.KeyboardPan.prototype, + 'unByKey', + ol.interaction.KeyboardPan.prototype.unByKey); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'getActive', + ol.interaction.KeyboardZoom.prototype.getActive); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'getMap', + ol.interaction.KeyboardZoom.prototype.getMap); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'setActive', + ol.interaction.KeyboardZoom.prototype.setActive); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'get', + ol.interaction.KeyboardZoom.prototype.get); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'getKeys', + ol.interaction.KeyboardZoom.prototype.getKeys); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'getProperties', + ol.interaction.KeyboardZoom.prototype.getProperties); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'set', + ol.interaction.KeyboardZoom.prototype.set); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'setProperties', + ol.interaction.KeyboardZoom.prototype.setProperties); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'unset', + ol.interaction.KeyboardZoom.prototype.unset); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'changed', + ol.interaction.KeyboardZoom.prototype.changed); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'dispatchEvent', + ol.interaction.KeyboardZoom.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'getRevision', + ol.interaction.KeyboardZoom.prototype.getRevision); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'on', + ol.interaction.KeyboardZoom.prototype.on); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'once', + ol.interaction.KeyboardZoom.prototype.once); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'un', + ol.interaction.KeyboardZoom.prototype.un); + +goog.exportProperty( + ol.interaction.KeyboardZoom.prototype, + 'unByKey', + ol.interaction.KeyboardZoom.prototype.unByKey); + +goog.exportProperty( + ol.interaction.ModifyEvent.prototype, + 'type', + ol.interaction.ModifyEvent.prototype.type); + +goog.exportProperty( + ol.interaction.ModifyEvent.prototype, + 'target', + ol.interaction.ModifyEvent.prototype.target); + +goog.exportProperty( + ol.interaction.ModifyEvent.prototype, + 'preventDefault', + ol.interaction.ModifyEvent.prototype.preventDefault); + +goog.exportProperty( + ol.interaction.ModifyEvent.prototype, + 'stopPropagation', + ol.interaction.ModifyEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'getActive', + ol.interaction.Modify.prototype.getActive); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'getMap', + ol.interaction.Modify.prototype.getMap); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'setActive', + ol.interaction.Modify.prototype.setActive); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'get', + ol.interaction.Modify.prototype.get); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'getKeys', + ol.interaction.Modify.prototype.getKeys); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'getProperties', + ol.interaction.Modify.prototype.getProperties); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'set', + ol.interaction.Modify.prototype.set); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'setProperties', + ol.interaction.Modify.prototype.setProperties); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'unset', + ol.interaction.Modify.prototype.unset); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'changed', + ol.interaction.Modify.prototype.changed); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'dispatchEvent', + ol.interaction.Modify.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'getRevision', + ol.interaction.Modify.prototype.getRevision); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'on', + ol.interaction.Modify.prototype.on); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'once', + ol.interaction.Modify.prototype.once); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'un', + ol.interaction.Modify.prototype.un); + +goog.exportProperty( + ol.interaction.Modify.prototype, + 'unByKey', + ol.interaction.Modify.prototype.unByKey); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'getActive', + ol.interaction.MouseWheelZoom.prototype.getActive); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'getMap', + ol.interaction.MouseWheelZoom.prototype.getMap); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'setActive', + ol.interaction.MouseWheelZoom.prototype.setActive); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'get', + ol.interaction.MouseWheelZoom.prototype.get); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'getKeys', + ol.interaction.MouseWheelZoom.prototype.getKeys); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'getProperties', + ol.interaction.MouseWheelZoom.prototype.getProperties); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'set', + ol.interaction.MouseWheelZoom.prototype.set); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'setProperties', + ol.interaction.MouseWheelZoom.prototype.setProperties); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'unset', + ol.interaction.MouseWheelZoom.prototype.unset); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'changed', + ol.interaction.MouseWheelZoom.prototype.changed); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'dispatchEvent', + ol.interaction.MouseWheelZoom.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'getRevision', + ol.interaction.MouseWheelZoom.prototype.getRevision); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'on', + ol.interaction.MouseWheelZoom.prototype.on); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'once', + ol.interaction.MouseWheelZoom.prototype.once); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'un', + ol.interaction.MouseWheelZoom.prototype.un); + +goog.exportProperty( + ol.interaction.MouseWheelZoom.prototype, + 'unByKey', + ol.interaction.MouseWheelZoom.prototype.unByKey); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'getActive', + ol.interaction.PinchRotate.prototype.getActive); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'getMap', + ol.interaction.PinchRotate.prototype.getMap); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'setActive', + ol.interaction.PinchRotate.prototype.setActive); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'get', + ol.interaction.PinchRotate.prototype.get); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'getKeys', + ol.interaction.PinchRotate.prototype.getKeys); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'getProperties', + ol.interaction.PinchRotate.prototype.getProperties); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'set', + ol.interaction.PinchRotate.prototype.set); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'setProperties', + ol.interaction.PinchRotate.prototype.setProperties); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'unset', + ol.interaction.PinchRotate.prototype.unset); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'changed', + ol.interaction.PinchRotate.prototype.changed); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'dispatchEvent', + ol.interaction.PinchRotate.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'getRevision', + ol.interaction.PinchRotate.prototype.getRevision); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'on', + ol.interaction.PinchRotate.prototype.on); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'once', + ol.interaction.PinchRotate.prototype.once); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'un', + ol.interaction.PinchRotate.prototype.un); + +goog.exportProperty( + ol.interaction.PinchRotate.prototype, + 'unByKey', + ol.interaction.PinchRotate.prototype.unByKey); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'getActive', + ol.interaction.PinchZoom.prototype.getActive); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'getMap', + ol.interaction.PinchZoom.prototype.getMap); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'setActive', + ol.interaction.PinchZoom.prototype.setActive); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'get', + ol.interaction.PinchZoom.prototype.get); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'getKeys', + ol.interaction.PinchZoom.prototype.getKeys); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'getProperties', + ol.interaction.PinchZoom.prototype.getProperties); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'set', + ol.interaction.PinchZoom.prototype.set); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'setProperties', + ol.interaction.PinchZoom.prototype.setProperties); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'unset', + ol.interaction.PinchZoom.prototype.unset); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'changed', + ol.interaction.PinchZoom.prototype.changed); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'dispatchEvent', + ol.interaction.PinchZoom.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'getRevision', + ol.interaction.PinchZoom.prototype.getRevision); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'on', + ol.interaction.PinchZoom.prototype.on); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'once', + ol.interaction.PinchZoom.prototype.once); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'un', + ol.interaction.PinchZoom.prototype.un); + +goog.exportProperty( + ol.interaction.PinchZoom.prototype, + 'unByKey', + ol.interaction.PinchZoom.prototype.unByKey); + +goog.exportProperty( + ol.interaction.SelectEvent.prototype, + 'type', + ol.interaction.SelectEvent.prototype.type); + +goog.exportProperty( + ol.interaction.SelectEvent.prototype, + 'target', + ol.interaction.SelectEvent.prototype.target); + +goog.exportProperty( + ol.interaction.SelectEvent.prototype, + 'preventDefault', + ol.interaction.SelectEvent.prototype.preventDefault); + +goog.exportProperty( + ol.interaction.SelectEvent.prototype, + 'stopPropagation', + ol.interaction.SelectEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'getActive', + ol.interaction.Select.prototype.getActive); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'getMap', + ol.interaction.Select.prototype.getMap); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'setActive', + ol.interaction.Select.prototype.setActive); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'get', + ol.interaction.Select.prototype.get); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'getKeys', + ol.interaction.Select.prototype.getKeys); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'getProperties', + ol.interaction.Select.prototype.getProperties); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'set', + ol.interaction.Select.prototype.set); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'setProperties', + ol.interaction.Select.prototype.setProperties); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'unset', + ol.interaction.Select.prototype.unset); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'changed', + ol.interaction.Select.prototype.changed); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'dispatchEvent', + ol.interaction.Select.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'getRevision', + ol.interaction.Select.prototype.getRevision); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'on', + ol.interaction.Select.prototype.on); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'once', + ol.interaction.Select.prototype.once); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'un', + ol.interaction.Select.prototype.un); + +goog.exportProperty( + ol.interaction.Select.prototype, + 'unByKey', + ol.interaction.Select.prototype.unByKey); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'getActive', + ol.interaction.Snap.prototype.getActive); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'getMap', + ol.interaction.Snap.prototype.getMap); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'setActive', + ol.interaction.Snap.prototype.setActive); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'get', + ol.interaction.Snap.prototype.get); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'getKeys', + ol.interaction.Snap.prototype.getKeys); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'getProperties', + ol.interaction.Snap.prototype.getProperties); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'set', + ol.interaction.Snap.prototype.set); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'setProperties', + ol.interaction.Snap.prototype.setProperties); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'unset', + ol.interaction.Snap.prototype.unset); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'changed', + ol.interaction.Snap.prototype.changed); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'dispatchEvent', + ol.interaction.Snap.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'getRevision', + ol.interaction.Snap.prototype.getRevision); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'on', + ol.interaction.Snap.prototype.on); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'once', + ol.interaction.Snap.prototype.once); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'un', + ol.interaction.Snap.prototype.un); + +goog.exportProperty( + ol.interaction.Snap.prototype, + 'unByKey', + ol.interaction.Snap.prototype.unByKey); + +goog.exportProperty( + ol.interaction.TranslateEvent.prototype, + 'type', + ol.interaction.TranslateEvent.prototype.type); + +goog.exportProperty( + ol.interaction.TranslateEvent.prototype, + 'target', + ol.interaction.TranslateEvent.prototype.target); + +goog.exportProperty( + ol.interaction.TranslateEvent.prototype, + 'preventDefault', + ol.interaction.TranslateEvent.prototype.preventDefault); + +goog.exportProperty( + ol.interaction.TranslateEvent.prototype, + 'stopPropagation', + ol.interaction.TranslateEvent.prototype.stopPropagation); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'getActive', + ol.interaction.Translate.prototype.getActive); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'getMap', + ol.interaction.Translate.prototype.getMap); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'setActive', + ol.interaction.Translate.prototype.setActive); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'get', + ol.interaction.Translate.prototype.get); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'getKeys', + ol.interaction.Translate.prototype.getKeys); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'getProperties', + ol.interaction.Translate.prototype.getProperties); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'set', + ol.interaction.Translate.prototype.set); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'setProperties', + ol.interaction.Translate.prototype.setProperties); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'unset', + ol.interaction.Translate.prototype.unset); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'changed', + ol.interaction.Translate.prototype.changed); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'dispatchEvent', + ol.interaction.Translate.prototype.dispatchEvent); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'getRevision', + ol.interaction.Translate.prototype.getRevision); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'on', + ol.interaction.Translate.prototype.on); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'once', + ol.interaction.Translate.prototype.once); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'un', + ol.interaction.Translate.prototype.un); + +goog.exportProperty( + ol.interaction.Translate.prototype, + 'unByKey', + ol.interaction.Translate.prototype.unByKey); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'get', + ol.geom.Geometry.prototype.get); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'getKeys', + ol.geom.Geometry.prototype.getKeys); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'getProperties', + ol.geom.Geometry.prototype.getProperties); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'set', + ol.geom.Geometry.prototype.set); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'setProperties', + ol.geom.Geometry.prototype.setProperties); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'unset', + ol.geom.Geometry.prototype.unset); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'changed', + ol.geom.Geometry.prototype.changed); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'dispatchEvent', + ol.geom.Geometry.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'getRevision', + ol.geom.Geometry.prototype.getRevision); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'on', + ol.geom.Geometry.prototype.on); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'once', + ol.geom.Geometry.prototype.once); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'un', + ol.geom.Geometry.prototype.un); + +goog.exportProperty( + ol.geom.Geometry.prototype, + 'unByKey', + ol.geom.Geometry.prototype.unByKey); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getClosestPoint', + ol.geom.SimpleGeometry.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getExtent', + ol.geom.SimpleGeometry.prototype.getExtent); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'rotate', + ol.geom.SimpleGeometry.prototype.rotate); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'simplify', + ol.geom.SimpleGeometry.prototype.simplify); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'transform', + ol.geom.SimpleGeometry.prototype.transform); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'get', + ol.geom.SimpleGeometry.prototype.get); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getKeys', + ol.geom.SimpleGeometry.prototype.getKeys); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getProperties', + ol.geom.SimpleGeometry.prototype.getProperties); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'set', + ol.geom.SimpleGeometry.prototype.set); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'setProperties', + ol.geom.SimpleGeometry.prototype.setProperties); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'unset', + ol.geom.SimpleGeometry.prototype.unset); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'changed', + ol.geom.SimpleGeometry.prototype.changed); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'dispatchEvent', + ol.geom.SimpleGeometry.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'getRevision', + ol.geom.SimpleGeometry.prototype.getRevision); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'on', + ol.geom.SimpleGeometry.prototype.on); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'once', + ol.geom.SimpleGeometry.prototype.once); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'un', + ol.geom.SimpleGeometry.prototype.un); + +goog.exportProperty( + ol.geom.SimpleGeometry.prototype, + 'unByKey', + ol.geom.SimpleGeometry.prototype.unByKey); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getFirstCoordinate', + ol.geom.Circle.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getLastCoordinate', + ol.geom.Circle.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getLayout', + ol.geom.Circle.prototype.getLayout); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'rotate', + ol.geom.Circle.prototype.rotate); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getClosestPoint', + ol.geom.Circle.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getExtent', + ol.geom.Circle.prototype.getExtent); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'simplify', + ol.geom.Circle.prototype.simplify); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'get', + ol.geom.Circle.prototype.get); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getKeys', + ol.geom.Circle.prototype.getKeys); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getProperties', + ol.geom.Circle.prototype.getProperties); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'set', + ol.geom.Circle.prototype.set); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'setProperties', + ol.geom.Circle.prototype.setProperties); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'unset', + ol.geom.Circle.prototype.unset); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'changed', + ol.geom.Circle.prototype.changed); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'dispatchEvent', + ol.geom.Circle.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'getRevision', + ol.geom.Circle.prototype.getRevision); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'on', + ol.geom.Circle.prototype.on); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'once', + ol.geom.Circle.prototype.once); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'un', + ol.geom.Circle.prototype.un); + +goog.exportProperty( + ol.geom.Circle.prototype, + 'unByKey', + ol.geom.Circle.prototype.unByKey); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'getClosestPoint', + ol.geom.GeometryCollection.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'getExtent', + ol.geom.GeometryCollection.prototype.getExtent); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'rotate', + ol.geom.GeometryCollection.prototype.rotate); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'simplify', + ol.geom.GeometryCollection.prototype.simplify); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'transform', + ol.geom.GeometryCollection.prototype.transform); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'get', + ol.geom.GeometryCollection.prototype.get); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'getKeys', + ol.geom.GeometryCollection.prototype.getKeys); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'getProperties', + ol.geom.GeometryCollection.prototype.getProperties); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'set', + ol.geom.GeometryCollection.prototype.set); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'setProperties', + ol.geom.GeometryCollection.prototype.setProperties); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'unset', + ol.geom.GeometryCollection.prototype.unset); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'changed', + ol.geom.GeometryCollection.prototype.changed); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'dispatchEvent', + ol.geom.GeometryCollection.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'getRevision', + ol.geom.GeometryCollection.prototype.getRevision); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'on', + ol.geom.GeometryCollection.prototype.on); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'once', + ol.geom.GeometryCollection.prototype.once); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'un', + ol.geom.GeometryCollection.prototype.un); + +goog.exportProperty( + ol.geom.GeometryCollection.prototype, + 'unByKey', + ol.geom.GeometryCollection.prototype.unByKey); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getFirstCoordinate', + ol.geom.LinearRing.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getLastCoordinate', + ol.geom.LinearRing.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getLayout', + ol.geom.LinearRing.prototype.getLayout); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'rotate', + ol.geom.LinearRing.prototype.rotate); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getClosestPoint', + ol.geom.LinearRing.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getExtent', + ol.geom.LinearRing.prototype.getExtent); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'simplify', + ol.geom.LinearRing.prototype.simplify); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'transform', + ol.geom.LinearRing.prototype.transform); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'get', + ol.geom.LinearRing.prototype.get); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getKeys', + ol.geom.LinearRing.prototype.getKeys); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getProperties', + ol.geom.LinearRing.prototype.getProperties); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'set', + ol.geom.LinearRing.prototype.set); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'setProperties', + ol.geom.LinearRing.prototype.setProperties); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'unset', + ol.geom.LinearRing.prototype.unset); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'changed', + ol.geom.LinearRing.prototype.changed); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'dispatchEvent', + ol.geom.LinearRing.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'getRevision', + ol.geom.LinearRing.prototype.getRevision); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'on', + ol.geom.LinearRing.prototype.on); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'once', + ol.geom.LinearRing.prototype.once); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'un', + ol.geom.LinearRing.prototype.un); + +goog.exportProperty( + ol.geom.LinearRing.prototype, + 'unByKey', + ol.geom.LinearRing.prototype.unByKey); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getFirstCoordinate', + ol.geom.LineString.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getLastCoordinate', + ol.geom.LineString.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getLayout', + ol.geom.LineString.prototype.getLayout); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'rotate', + ol.geom.LineString.prototype.rotate); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getClosestPoint', + ol.geom.LineString.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getExtent', + ol.geom.LineString.prototype.getExtent); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'simplify', + ol.geom.LineString.prototype.simplify); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'transform', + ol.geom.LineString.prototype.transform); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'get', + ol.geom.LineString.prototype.get); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getKeys', + ol.geom.LineString.prototype.getKeys); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getProperties', + ol.geom.LineString.prototype.getProperties); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'set', + ol.geom.LineString.prototype.set); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'setProperties', + ol.geom.LineString.prototype.setProperties); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'unset', + ol.geom.LineString.prototype.unset); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'changed', + ol.geom.LineString.prototype.changed); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'dispatchEvent', + ol.geom.LineString.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'getRevision', + ol.geom.LineString.prototype.getRevision); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'on', + ol.geom.LineString.prototype.on); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'once', + ol.geom.LineString.prototype.once); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'un', + ol.geom.LineString.prototype.un); + +goog.exportProperty( + ol.geom.LineString.prototype, + 'unByKey', + ol.geom.LineString.prototype.unByKey); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getFirstCoordinate', + ol.geom.MultiLineString.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getLastCoordinate', + ol.geom.MultiLineString.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getLayout', + ol.geom.MultiLineString.prototype.getLayout); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'rotate', + ol.geom.MultiLineString.prototype.rotate); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getClosestPoint', + ol.geom.MultiLineString.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getExtent', + ol.geom.MultiLineString.prototype.getExtent); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'simplify', + ol.geom.MultiLineString.prototype.simplify); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'transform', + ol.geom.MultiLineString.prototype.transform); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'get', + ol.geom.MultiLineString.prototype.get); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getKeys', + ol.geom.MultiLineString.prototype.getKeys); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getProperties', + ol.geom.MultiLineString.prototype.getProperties); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'set', + ol.geom.MultiLineString.prototype.set); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'setProperties', + ol.geom.MultiLineString.prototype.setProperties); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'unset', + ol.geom.MultiLineString.prototype.unset); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'changed', + ol.geom.MultiLineString.prototype.changed); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'dispatchEvent', + ol.geom.MultiLineString.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'getRevision', + ol.geom.MultiLineString.prototype.getRevision); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'on', + ol.geom.MultiLineString.prototype.on); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'once', + ol.geom.MultiLineString.prototype.once); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'un', + ol.geom.MultiLineString.prototype.un); + +goog.exportProperty( + ol.geom.MultiLineString.prototype, + 'unByKey', + ol.geom.MultiLineString.prototype.unByKey); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getFirstCoordinate', + ol.geom.MultiPoint.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getLastCoordinate', + ol.geom.MultiPoint.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getLayout', + ol.geom.MultiPoint.prototype.getLayout); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'rotate', + ol.geom.MultiPoint.prototype.rotate); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getClosestPoint', + ol.geom.MultiPoint.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getExtent', + ol.geom.MultiPoint.prototype.getExtent); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'simplify', + ol.geom.MultiPoint.prototype.simplify); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'transform', + ol.geom.MultiPoint.prototype.transform); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'get', + ol.geom.MultiPoint.prototype.get); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getKeys', + ol.geom.MultiPoint.prototype.getKeys); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getProperties', + ol.geom.MultiPoint.prototype.getProperties); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'set', + ol.geom.MultiPoint.prototype.set); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'setProperties', + ol.geom.MultiPoint.prototype.setProperties); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'unset', + ol.geom.MultiPoint.prototype.unset); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'changed', + ol.geom.MultiPoint.prototype.changed); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'dispatchEvent', + ol.geom.MultiPoint.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'getRevision', + ol.geom.MultiPoint.prototype.getRevision); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'on', + ol.geom.MultiPoint.prototype.on); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'once', + ol.geom.MultiPoint.prototype.once); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'un', + ol.geom.MultiPoint.prototype.un); + +goog.exportProperty( + ol.geom.MultiPoint.prototype, + 'unByKey', + ol.geom.MultiPoint.prototype.unByKey); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getFirstCoordinate', + ol.geom.MultiPolygon.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getLastCoordinate', + ol.geom.MultiPolygon.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getLayout', + ol.geom.MultiPolygon.prototype.getLayout); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'rotate', + ol.geom.MultiPolygon.prototype.rotate); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getClosestPoint', + ol.geom.MultiPolygon.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getExtent', + ol.geom.MultiPolygon.prototype.getExtent); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'simplify', + ol.geom.MultiPolygon.prototype.simplify); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'transform', + ol.geom.MultiPolygon.prototype.transform); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'get', + ol.geom.MultiPolygon.prototype.get); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getKeys', + ol.geom.MultiPolygon.prototype.getKeys); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getProperties', + ol.geom.MultiPolygon.prototype.getProperties); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'set', + ol.geom.MultiPolygon.prototype.set); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'setProperties', + ol.geom.MultiPolygon.prototype.setProperties); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'unset', + ol.geom.MultiPolygon.prototype.unset); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'changed', + ol.geom.MultiPolygon.prototype.changed); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'dispatchEvent', + ol.geom.MultiPolygon.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'getRevision', + ol.geom.MultiPolygon.prototype.getRevision); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'on', + ol.geom.MultiPolygon.prototype.on); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'once', + ol.geom.MultiPolygon.prototype.once); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'un', + ol.geom.MultiPolygon.prototype.un); + +goog.exportProperty( + ol.geom.MultiPolygon.prototype, + 'unByKey', + ol.geom.MultiPolygon.prototype.unByKey); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getFirstCoordinate', + ol.geom.Point.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getLastCoordinate', + ol.geom.Point.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getLayout', + ol.geom.Point.prototype.getLayout); + +goog.exportProperty( + ol.geom.Point.prototype, + 'rotate', + ol.geom.Point.prototype.rotate); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getClosestPoint', + ol.geom.Point.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getExtent', + ol.geom.Point.prototype.getExtent); + +goog.exportProperty( + ol.geom.Point.prototype, + 'simplify', + ol.geom.Point.prototype.simplify); + +goog.exportProperty( + ol.geom.Point.prototype, + 'transform', + ol.geom.Point.prototype.transform); + +goog.exportProperty( + ol.geom.Point.prototype, + 'get', + ol.geom.Point.prototype.get); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getKeys', + ol.geom.Point.prototype.getKeys); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getProperties', + ol.geom.Point.prototype.getProperties); + +goog.exportProperty( + ol.geom.Point.prototype, + 'set', + ol.geom.Point.prototype.set); + +goog.exportProperty( + ol.geom.Point.prototype, + 'setProperties', + ol.geom.Point.prototype.setProperties); + +goog.exportProperty( + ol.geom.Point.prototype, + 'unset', + ol.geom.Point.prototype.unset); + +goog.exportProperty( + ol.geom.Point.prototype, + 'changed', + ol.geom.Point.prototype.changed); + +goog.exportProperty( + ol.geom.Point.prototype, + 'dispatchEvent', + ol.geom.Point.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.Point.prototype, + 'getRevision', + ol.geom.Point.prototype.getRevision); + +goog.exportProperty( + ol.geom.Point.prototype, + 'on', + ol.geom.Point.prototype.on); + +goog.exportProperty( + ol.geom.Point.prototype, + 'once', + ol.geom.Point.prototype.once); + +goog.exportProperty( + ol.geom.Point.prototype, + 'un', + ol.geom.Point.prototype.un); + +goog.exportProperty( + ol.geom.Point.prototype, + 'unByKey', + ol.geom.Point.prototype.unByKey); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getFirstCoordinate', + ol.geom.Polygon.prototype.getFirstCoordinate); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getLastCoordinate', + ol.geom.Polygon.prototype.getLastCoordinate); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getLayout', + ol.geom.Polygon.prototype.getLayout); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'rotate', + ol.geom.Polygon.prototype.rotate); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getClosestPoint', + ol.geom.Polygon.prototype.getClosestPoint); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getExtent', + ol.geom.Polygon.prototype.getExtent); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'simplify', + ol.geom.Polygon.prototype.simplify); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'transform', + ol.geom.Polygon.prototype.transform); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'get', + ol.geom.Polygon.prototype.get); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getKeys', + ol.geom.Polygon.prototype.getKeys); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getProperties', + ol.geom.Polygon.prototype.getProperties); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'set', + ol.geom.Polygon.prototype.set); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'setProperties', + ol.geom.Polygon.prototype.setProperties); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'unset', + ol.geom.Polygon.prototype.unset); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'changed', + ol.geom.Polygon.prototype.changed); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'dispatchEvent', + ol.geom.Polygon.prototype.dispatchEvent); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'getRevision', + ol.geom.Polygon.prototype.getRevision); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'on', + ol.geom.Polygon.prototype.on); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'once', + ol.geom.Polygon.prototype.once); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'un', + ol.geom.Polygon.prototype.un); + +goog.exportProperty( + ol.geom.Polygon.prototype, + 'unByKey', + ol.geom.Polygon.prototype.unByKey); + +goog.exportProperty( + ol.format.GML2.prototype, + 'readFeatures', + ol.format.GML2.prototype.readFeatures); + +goog.exportProperty( + ol.format.GML3.prototype, + 'readFeatures', + ol.format.GML3.prototype.readFeatures); + +goog.exportProperty( + ol.format.GML.prototype, + 'readFeatures', + ol.format.GML.prototype.readFeatures); + +goog.exportProperty( + ol.control.Control.prototype, + 'get', + ol.control.Control.prototype.get); + +goog.exportProperty( + ol.control.Control.prototype, + 'getKeys', + ol.control.Control.prototype.getKeys); + +goog.exportProperty( + ol.control.Control.prototype, + 'getProperties', + ol.control.Control.prototype.getProperties); + +goog.exportProperty( + ol.control.Control.prototype, + 'set', + ol.control.Control.prototype.set); + +goog.exportProperty( + ol.control.Control.prototype, + 'setProperties', + ol.control.Control.prototype.setProperties); + +goog.exportProperty( + ol.control.Control.prototype, + 'unset', + ol.control.Control.prototype.unset); + +goog.exportProperty( + ol.control.Control.prototype, + 'changed', + ol.control.Control.prototype.changed); + +goog.exportProperty( + ol.control.Control.prototype, + 'dispatchEvent', + ol.control.Control.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.Control.prototype, + 'getRevision', + ol.control.Control.prototype.getRevision); + +goog.exportProperty( + ol.control.Control.prototype, + 'on', + ol.control.Control.prototype.on); + +goog.exportProperty( + ol.control.Control.prototype, + 'once', + ol.control.Control.prototype.once); + +goog.exportProperty( + ol.control.Control.prototype, + 'un', + ol.control.Control.prototype.un); + +goog.exportProperty( + ol.control.Control.prototype, + 'unByKey', + ol.control.Control.prototype.unByKey); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'getMap', + ol.control.Attribution.prototype.getMap); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'setMap', + ol.control.Attribution.prototype.setMap); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'setTarget', + ol.control.Attribution.prototype.setTarget); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'get', + ol.control.Attribution.prototype.get); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'getKeys', + ol.control.Attribution.prototype.getKeys); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'getProperties', + ol.control.Attribution.prototype.getProperties); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'set', + ol.control.Attribution.prototype.set); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'setProperties', + ol.control.Attribution.prototype.setProperties); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'unset', + ol.control.Attribution.prototype.unset); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'changed', + ol.control.Attribution.prototype.changed); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'dispatchEvent', + ol.control.Attribution.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'getRevision', + ol.control.Attribution.prototype.getRevision); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'on', + ol.control.Attribution.prototype.on); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'once', + ol.control.Attribution.prototype.once); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'un', + ol.control.Attribution.prototype.un); + +goog.exportProperty( + ol.control.Attribution.prototype, + 'unByKey', + ol.control.Attribution.prototype.unByKey); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'getMap', + ol.control.FullScreen.prototype.getMap); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'setMap', + ol.control.FullScreen.prototype.setMap); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'setTarget', + ol.control.FullScreen.prototype.setTarget); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'get', + ol.control.FullScreen.prototype.get); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'getKeys', + ol.control.FullScreen.prototype.getKeys); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'getProperties', + ol.control.FullScreen.prototype.getProperties); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'set', + ol.control.FullScreen.prototype.set); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'setProperties', + ol.control.FullScreen.prototype.setProperties); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'unset', + ol.control.FullScreen.prototype.unset); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'changed', + ol.control.FullScreen.prototype.changed); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'dispatchEvent', + ol.control.FullScreen.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'getRevision', + ol.control.FullScreen.prototype.getRevision); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'on', + ol.control.FullScreen.prototype.on); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'once', + ol.control.FullScreen.prototype.once); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'un', + ol.control.FullScreen.prototype.un); + +goog.exportProperty( + ol.control.FullScreen.prototype, + 'unByKey', + ol.control.FullScreen.prototype.unByKey); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'getMap', + ol.control.MousePosition.prototype.getMap); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'setMap', + ol.control.MousePosition.prototype.setMap); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'setTarget', + ol.control.MousePosition.prototype.setTarget); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'get', + ol.control.MousePosition.prototype.get); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'getKeys', + ol.control.MousePosition.prototype.getKeys); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'getProperties', + ol.control.MousePosition.prototype.getProperties); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'set', + ol.control.MousePosition.prototype.set); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'setProperties', + ol.control.MousePosition.prototype.setProperties); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'unset', + ol.control.MousePosition.prototype.unset); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'changed', + ol.control.MousePosition.prototype.changed); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'dispatchEvent', + ol.control.MousePosition.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'getRevision', + ol.control.MousePosition.prototype.getRevision); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'on', + ol.control.MousePosition.prototype.on); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'once', + ol.control.MousePosition.prototype.once); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'un', + ol.control.MousePosition.prototype.un); + +goog.exportProperty( + ol.control.MousePosition.prototype, + 'unByKey', + ol.control.MousePosition.prototype.unByKey); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'getMap', + ol.control.OverviewMap.prototype.getMap); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'setMap', + ol.control.OverviewMap.prototype.setMap); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'setTarget', + ol.control.OverviewMap.prototype.setTarget); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'get', + ol.control.OverviewMap.prototype.get); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'getKeys', + ol.control.OverviewMap.prototype.getKeys); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'getProperties', + ol.control.OverviewMap.prototype.getProperties); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'set', + ol.control.OverviewMap.prototype.set); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'setProperties', + ol.control.OverviewMap.prototype.setProperties); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'unset', + ol.control.OverviewMap.prototype.unset); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'changed', + ol.control.OverviewMap.prototype.changed); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'dispatchEvent', + ol.control.OverviewMap.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'getRevision', + ol.control.OverviewMap.prototype.getRevision); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'on', + ol.control.OverviewMap.prototype.on); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'once', + ol.control.OverviewMap.prototype.once); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'un', + ol.control.OverviewMap.prototype.un); + +goog.exportProperty( + ol.control.OverviewMap.prototype, + 'unByKey', + ol.control.OverviewMap.prototype.unByKey); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'getMap', + ol.control.Rotate.prototype.getMap); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'setMap', + ol.control.Rotate.prototype.setMap); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'setTarget', + ol.control.Rotate.prototype.setTarget); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'get', + ol.control.Rotate.prototype.get); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'getKeys', + ol.control.Rotate.prototype.getKeys); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'getProperties', + ol.control.Rotate.prototype.getProperties); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'set', + ol.control.Rotate.prototype.set); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'setProperties', + ol.control.Rotate.prototype.setProperties); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'unset', + ol.control.Rotate.prototype.unset); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'changed', + ol.control.Rotate.prototype.changed); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'dispatchEvent', + ol.control.Rotate.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'getRevision', + ol.control.Rotate.prototype.getRevision); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'on', + ol.control.Rotate.prototype.on); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'once', + ol.control.Rotate.prototype.once); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'un', + ol.control.Rotate.prototype.un); + +goog.exportProperty( + ol.control.Rotate.prototype, + 'unByKey', + ol.control.Rotate.prototype.unByKey); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'getMap', + ol.control.ScaleLine.prototype.getMap); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'setMap', + ol.control.ScaleLine.prototype.setMap); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'setTarget', + ol.control.ScaleLine.prototype.setTarget); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'get', + ol.control.ScaleLine.prototype.get); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'getKeys', + ol.control.ScaleLine.prototype.getKeys); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'getProperties', + ol.control.ScaleLine.prototype.getProperties); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'set', + ol.control.ScaleLine.prototype.set); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'setProperties', + ol.control.ScaleLine.prototype.setProperties); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'unset', + ol.control.ScaleLine.prototype.unset); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'changed', + ol.control.ScaleLine.prototype.changed); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'dispatchEvent', + ol.control.ScaleLine.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'getRevision', + ol.control.ScaleLine.prototype.getRevision); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'on', + ol.control.ScaleLine.prototype.on); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'once', + ol.control.ScaleLine.prototype.once); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'un', + ol.control.ScaleLine.prototype.un); + +goog.exportProperty( + ol.control.ScaleLine.prototype, + 'unByKey', + ol.control.ScaleLine.prototype.unByKey); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'getMap', + ol.control.Zoom.prototype.getMap); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'setMap', + ol.control.Zoom.prototype.setMap); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'setTarget', + ol.control.Zoom.prototype.setTarget); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'get', + ol.control.Zoom.prototype.get); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'getKeys', + ol.control.Zoom.prototype.getKeys); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'getProperties', + ol.control.Zoom.prototype.getProperties); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'set', + ol.control.Zoom.prototype.set); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'setProperties', + ol.control.Zoom.prototype.setProperties); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'unset', + ol.control.Zoom.prototype.unset); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'changed', + ol.control.Zoom.prototype.changed); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'dispatchEvent', + ol.control.Zoom.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'getRevision', + ol.control.Zoom.prototype.getRevision); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'on', + ol.control.Zoom.prototype.on); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'once', + ol.control.Zoom.prototype.once); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'un', + ol.control.Zoom.prototype.un); + +goog.exportProperty( + ol.control.Zoom.prototype, + 'unByKey', + ol.control.Zoom.prototype.unByKey); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'getMap', + ol.control.ZoomSlider.prototype.getMap); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'setMap', + ol.control.ZoomSlider.prototype.setMap); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'setTarget', + ol.control.ZoomSlider.prototype.setTarget); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'get', + ol.control.ZoomSlider.prototype.get); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'getKeys', + ol.control.ZoomSlider.prototype.getKeys); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'getProperties', + ol.control.ZoomSlider.prototype.getProperties); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'set', + ol.control.ZoomSlider.prototype.set); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'setProperties', + ol.control.ZoomSlider.prototype.setProperties); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'unset', + ol.control.ZoomSlider.prototype.unset); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'changed', + ol.control.ZoomSlider.prototype.changed); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'dispatchEvent', + ol.control.ZoomSlider.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'getRevision', + ol.control.ZoomSlider.prototype.getRevision); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'on', + ol.control.ZoomSlider.prototype.on); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'once', + ol.control.ZoomSlider.prototype.once); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'un', + ol.control.ZoomSlider.prototype.un); + +goog.exportProperty( + ol.control.ZoomSlider.prototype, + 'unByKey', + ol.control.ZoomSlider.prototype.unByKey); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'getMap', + ol.control.ZoomToExtent.prototype.getMap); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'setMap', + ol.control.ZoomToExtent.prototype.setMap); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'setTarget', + ol.control.ZoomToExtent.prototype.setTarget); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'get', + ol.control.ZoomToExtent.prototype.get); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'getKeys', + ol.control.ZoomToExtent.prototype.getKeys); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'getProperties', + ol.control.ZoomToExtent.prototype.getProperties); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'set', + ol.control.ZoomToExtent.prototype.set); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'setProperties', + ol.control.ZoomToExtent.prototype.setProperties); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'unset', + ol.control.ZoomToExtent.prototype.unset); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'changed', + ol.control.ZoomToExtent.prototype.changed); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'dispatchEvent', + ol.control.ZoomToExtent.prototype.dispatchEvent); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'getRevision', + ol.control.ZoomToExtent.prototype.getRevision); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'on', + ol.control.ZoomToExtent.prototype.on); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'once', + ol.control.ZoomToExtent.prototype.once); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'un', + ol.control.ZoomToExtent.prototype.un); + +goog.exportProperty( + ol.control.ZoomToExtent.prototype, + 'unByKey', + ol.control.ZoomToExtent.prototype.unByKey); +OPENLAYERS.ol = ol; + + return OPENLAYERS.ol; +})); diff --git a/themes/bootstrap3/js/vendor/ol/ol.js b/themes/bootstrap3/js/vendor/ol/ol.js new file mode 100644 index 0000000000000000000000000000000000000000..c552dcc751e6fe7a71c595bf9b4342d4b319aeea --- /dev/null +++ b/themes/bootstrap3/js/vendor/ol/ol.js @@ -0,0 +1,998 @@ +// OpenLayers 3. See http://openlayers.org/ +// License: https://raw.githubusercontent.com/openlayers/ol3/master/LICENSE.md +// Version: v3.17.1 + +(function (root, factory) { + if (typeof exports === "object") { + module.exports = factory(); + } else if (typeof define === "function" && define.amd) { + define([], factory); + } else { + root.ol = factory(); + } +}(this, function () { + var OPENLAYERS = {}; + var k,aa=this;function t(a,b,c){a=a.split(".");c=c||aa;a[0]in c||!c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]?c=c[d]:c=c[d]={}:c[d]=b}function ba(a){a.Zb=function(){return a.Tg?a.Tg:a.Tg=new a}} +function ca(a){var b=typeof a;if("object"==b)if(a){if(a instanceof Array)return"array";if(a instanceof Object)return b;var c=Object.prototype.toString.call(a);if("[object Window]"==c)return"object";if("[object Array]"==c||"number"==typeof a.length&&"undefined"!=typeof a.splice&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("splice"))return"array";if("[object Function]"==c||"undefined"!=typeof a.call&&"undefined"!=typeof a.propertyIsEnumerable&&!a.propertyIsEnumerable("call"))return"function"}else return"null"; +else if("function"==b&&"undefined"==typeof a.call)return"object";return b}function da(a){return"string"==typeof a}function ea(a){return"number"==typeof a}function fa(a){var b=typeof a;return"object"==b&&null!=a||"function"==b}function w(a){return a[ga]||(a[ga]=++ha)}var ga="closure_uid_"+(1E9*Math.random()>>>0),ha=0;function ia(a,b,c){return a.call.apply(a.bind,arguments)} +function ja(a,b,c){if(!a)throw Error();if(2<arguments.length){var d=Array.prototype.slice.call(arguments,2);return function(){var c=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(c,d);return a.apply(b,c)}}return function(){return a.apply(b,arguments)}}function ka(a,b,c){ka=Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf("native code")?ia:ja;return ka.apply(null,arguments)};var la,ma;function y(a,b){a.prototype=Object.create(b.prototype);a.prototype.constructor=a}function na(){}var pa=Function("return this")();var qa=String.prototype.trim?function(a){return a.trim()}:function(a){return a.replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")};function ra(a,b){return a<b?-1:a>b?1:0};function sa(a,b,c){return Math.min(Math.max(a,b),c)}var ta=function(){var a;"cosh"in Math?a=Math.cosh:a=function(a){a=Math.exp(a);return(a+1/a)/2};return a}();function ua(a,b,c,d,e,f){var g=e-c,h=f-d;if(0!==g||0!==h){var l=((a-c)*g+(b-d)*h)/(g*g+h*h);1<l?(c=e,d=f):0<l&&(c+=g*l,d+=h*l)}return va(a,b,c,d)}function va(a,b,c,d){a=c-a;b=d-b;return a*a+b*b}function wa(a){return a*Math.PI/180}function xa(a,b){var c=a%b;return 0>c*b?c+b:c}function za(a,b,c){return a+c*(b-a)};function Ba(a){return function(b){if(b)return[sa(b[0],a[0],a[2]),sa(b[1],a[1],a[3])]}}function Ca(a){return a};function Da(a,b,c){this.center=a;this.resolution=b;this.rotation=c};var Ea="function"===typeof Object.assign?Object.assign:function(a,b){if(!a||!a)throw new TypeError("Cannot convert undefined or null to object");for(var c=Object(a),d=1,e=arguments.length;d<e;++d){var f=arguments[d];if(void 0!==f&&null!==f)for(var g in f)f.hasOwnProperty(g)&&(c[g]=f[g])}return c};function Fa(a){for(var b in a)delete a[b]}function Ga(a){var b=[],c;for(c in a)b.push(a[c]);return b}function Ha(a){for(var b in a)return!1;return!b};var Ia="olm_"+(1E4*Math.random()|0);function Ja(a){function b(b){var d=a.listener,e=a.ng||a.target;a.pg&&Ka(a);return d.call(e,b)}return a.og=b}function La(a,b,c,d){for(var e,f=0,g=a.length;f<g;++f)if(e=a[f],e.listener===b&&e.ng===c)return d&&(e.deleteIndex=f),e}function Ma(a,b){var c=a[Ia];return c?c[b]:void 0}function Na(a){var b=a[Ia];b||(b=a[Ia]={});return b} +function Oa(a,b){var c=Ma(a,b);if(c){for(var d=0,e=c.length;d<e;++d)a.removeEventListener(b,c[d].og),Fa(c[d]);c.length=0;if(c=a[Ia])delete c[b],0===Object.keys(c).length&&delete a[Ia]}}function B(a,b,c,d,e){var f=Na(a),g=f[b];g||(g=f[b]=[]);(f=La(g,c,d,!1))?e||(f.pg=!1):(f={ng:d,pg:!!e,listener:c,target:a,type:b},a.addEventListener(b,Ja(f)),g.push(f));return f}function Pa(a,b,c,d){return B(a,b,c,d,!0)}function Qa(a,b,c,d){(a=Ma(a,b))&&(c=La(a,c,d,!0))&&Ka(c)} +function Ka(a){if(a&&a.target){a.target.removeEventListener(a.type,a.og);var b=Ma(a.target,a.type);if(b){var c="deleteIndex"in a?a.deleteIndex:b.indexOf(a);-1!==c&&b.splice(c,1);0===b.length&&Oa(a.target,a.type)}Fa(a)}}function Ra(a){var b=Na(a),c;for(c in b)Oa(a,c)};function Sa(){}Sa.prototype.Gb=!1;function Ta(a){a.Gb||(a.Gb=!0,a.ka())}Sa.prototype.ka=na;function Wa(a,b){this.type=a;this.target=b||null}Wa.prototype.preventDefault=Wa.prototype.stopPropagation=function(){this.to=!0};function Ya(a){a.stopPropagation()}function Za(a){a.preventDefault()};function $a(){this.Ra={};this.Ba={};this.ya={}}y($a,Sa);$a.prototype.addEventListener=function(a,b){var c=this.ya[a];c||(c=this.ya[a]=[]);-1===c.indexOf(b)&&c.push(b)}; +$a.prototype.b=function(a){var b="string"===typeof a?new Wa(a):a;a=b.type;b.target=this;var c=this.ya[a],d;if(c){a in this.Ba||(this.Ba[a]=0,this.Ra[a]=0);++this.Ba[a];for(var e=0,f=c.length;e<f;++e)if(!1===c[e].call(this,b)||b.to){d=!1;break}--this.Ba[a];if(0===this.Ba[a]){b=this.Ra[a];for(delete this.Ra[a];b--;)this.removeEventListener(a,na);delete this.Ba[a]}return d}};$a.prototype.ka=function(){Ra(this)};function ab(a,b){return b?b in a.ya:0<Object.keys(a.ya).length} +$a.prototype.removeEventListener=function(a,b){var c=this.ya[a];if(c){var d=c.indexOf(b);a in this.Ra?(c[d]=na,++this.Ra[a]):(c.splice(d,1),0===c.length&&delete this.ya[a])}};function bb(){$a.call(this);this.g=0}y(bb,$a);function cb(a){if(Array.isArray(a))for(var b=0,c=a.length;b<c;++b)Ka(a[b]);else Ka(a)}k=bb.prototype;k.u=function(){++this.g;this.b("change")};k.K=function(){return this.g};k.I=function(a,b,c){if(Array.isArray(a)){for(var d=a.length,e=Array(d),f=0;f<d;++f)e[f]=B(this,a[f],b,c);return e}return B(this,a,b,c)};k.L=function(a,b,c){if(Array.isArray(a)){for(var d=a.length,e=Array(d),f=0;f<d;++f)e[f]=Pa(this,a[f],b,c);return e}return Pa(this,a,b,c)}; +k.J=function(a,b,c){if(Array.isArray(a))for(var d=0,e=a.length;d<e;++d)Qa(this,a[d],b,c);else Qa(this,a,b,c)};k.M=cb;function db(a,b,c){Wa.call(this,a);this.key=b;this.oldValue=c}y(db,Wa);function eb(a){bb.call(this);w(this);this.U={};void 0!==a&&this.G(a)}y(eb,bb);var fb={};function gb(a){return fb.hasOwnProperty(a)?fb[a]:fb[a]="change:"+a}k=eb.prototype;k.get=function(a){var b;this.U.hasOwnProperty(a)&&(b=this.U[a]);return b};k.N=function(){return Object.keys(this.U)};k.O=function(){return Ea({},this.U)};function hb(a,b,c){var d;d=gb(b);a.b(new db(d,b,c));a.b(new db("propertychange",b,c))} +k.set=function(a,b,c){c?this.U[a]=b:(c=this.U[a],this.U[a]=b,c!==b&&hb(this,a,c))};k.G=function(a,b){for(var c in a)this.set(c,a[c],b)};k.P=function(a,b){if(a in this.U){var c=this.U[a];delete this.U[a];b||hb(this,a,c)}};function ib(a,b){return a>b?1:a<b?-1:0}function jb(a,b){return 0<=a.indexOf(b)}function kb(a,b,c){var d=a.length;if(a[0]<=b)return 0;if(!(b<=a[d-1]))if(0<c)for(c=1;c<d;++c){if(a[c]<b)return c-1}else if(0>c)for(c=1;c<d;++c){if(a[c]<=b)return c}else for(c=1;c<d;++c){if(a[c]==b)return c;if(a[c]<b)return a[c-1]-b<b-a[c]?c-1:c}return d-1}function lb(a){return a.reduce(function(a,c){return Array.isArray(c)?a.concat(lb(c)):a.concat(c)},[])} +function mb(a,b){var c;c=ca(b);var d="array"==c||"object"==c&&"number"==typeof b.length?b:[b],e=d.length;for(c=0;c<e;c++)a[a.length]=d[c]}function nb(a,b){var c=a.indexOf(b),d=-1<c;d&&a.splice(c,1);return d}function ob(a,b){for(var c=a.length>>>0,d,e=0;e<c;e++)if(d=a[e],b(d,e,a))return d;return null}function pb(a,b){var c=a.length;if(c!==b.length)return!1;for(var d=0;d<c;d++)if(a[d]!==b[d])return!1;return!0} +function qb(a){var b=rb,c=a.length,d=Array(a.length),e;for(e=0;e<c;e++)d[e]={index:e,value:a[e]};d.sort(function(a,c){return b(a.value,c.value)||a.index-c.index});for(e=0;e<a.length;e++)a[e]=d[e].value}function sb(a,b){var c;return a.every(function(d,e){c=e;return!b(d,e,a)})?-1:c};function tb(a){return function(b,c,d){if(void 0!==b)return b=kb(a,b,d),b=sa(b+c,0,a.length-1),a[b]}}function ub(a,b,c){return function(d,e,f){if(void 0!==d)return d=Math.max(Math.floor(Math.log(b/d)/Math.log(a)+(0<f?0:0>f?1:.5))+e,0),void 0!==c&&(d=Math.min(d,c)),b/Math.pow(a,d)}};function vb(a){if(void 0!==a)return 0}function wb(a,b){if(void 0!==a)return a+b}function xb(a){var b=2*Math.PI/a;return function(a,d){if(void 0!==a)return a=Math.floor((a+d)/b+.5)*b}}function yb(){var a=wa(5);return function(b,c){if(void 0!==b)return Math.abs(b+c)<=a?0:b+c}};function zb(a,b){var c=void 0!==b?a.toFixed(b):""+a,d=c.indexOf("."),d=-1===d?c.length:d;return 2<d?c:Array(3-d).join("0")+c}function Ab(a){a=(""+a).split(".");for(var b=["1","3"],c=0;c<Math.max(a.length,b.length);c++){var d=parseInt(a[c]||"0",10),e=parseInt(b[c]||"0",10);if(d>e)return 1;if(e>d)return-1}return 0};function Bb(a,b){a[0]+=b[0];a[1]+=b[1];return a}function Cb(a,b){var c=a[0],d=a[1],e=b[0],f=b[1],g=e[0],e=e[1],h=f[0],f=f[1],l=h-g,m=f-e,c=0===l&&0===m?0:(l*(c-g)+m*(d-e))/(l*l+m*m||0);0>=c||(1<=c?(g=h,e=f):(g+=c*l,e+=c*m));return[g,e]}function Db(a,b,c){a=xa(a+180,360)-180;var d=Math.abs(3600*a);return Math.floor(d/3600)+"\u00b0 "+zb(Math.floor(d/60%60))+"\u2032 "+zb(d%60,c||0)+"\u2033 "+b.charAt(0>a?1:0)} +function Eb(a,b,c){return a?b.replace("{x}",a[0].toFixed(c)).replace("{y}",a[1].toFixed(c)):""}function Fb(a,b){for(var c=!0,d=a.length-1;0<=d;--d)if(a[d]!=b[d]){c=!1;break}return c}function Gb(a,b){var c=Math.cos(b),d=Math.sin(b),e=a[1]*c+a[0]*d;a[0]=a[0]*c-a[1]*d;a[1]=e;return a}function Hb(a,b){var c=a[0]-b[0],d=a[1]-b[1];return c*c+d*d}function Ib(a,b){return Hb(a,Cb(a,b))}function Jb(a,b){return Eb(a,"{x}, {y}",b)};function Kb(a){for(var b=Lb(),c=0,d=a.length;c<d;++c)Mb(b,a[c]);return b}function Ob(a,b,c){return c?(c[0]=a[0]-b,c[1]=a[1]-b,c[2]=a[2]+b,c[3]=a[3]+b,c):[a[0]-b,a[1]-b,a[2]+b,a[3]+b]}function Pb(a,b){return b?(b[0]=a[0],b[1]=a[1],b[2]=a[2],b[3]=a[3],b):a.slice()}function Rb(a,b,c){b=b<a[0]?a[0]-b:a[2]<b?b-a[2]:0;a=c<a[1]?a[1]-c:a[3]<c?c-a[3]:0;return b*b+a*a}function Sb(a,b){return Tb(a,b[0],b[1])}function Ub(a,b){return a[0]<=b[0]&&b[2]<=a[2]&&a[1]<=b[1]&&b[3]<=a[3]} +function Tb(a,b,c){return a[0]<=b&&b<=a[2]&&a[1]<=c&&c<=a[3]}function Vb(a,b){var c=a[1],d=a[2],e=a[3],f=b[0],g=b[1],h=0;f<a[0]?h|=16:f>d&&(h|=4);g<c?h|=8:g>e&&(h|=2);0===h&&(h=1);return h}function Lb(){return[Infinity,Infinity,-Infinity,-Infinity]}function Wb(a,b,c,d,e){return e?(e[0]=a,e[1]=b,e[2]=c,e[3]=d,e):[a,b,c,d]}function Xb(a,b){var c=a[0],d=a[1];return Wb(c,d,c,d,b)}function Yb(a,b,c,d,e){e=Wb(Infinity,Infinity,-Infinity,-Infinity,e);return Zb(e,a,b,c,d)} +function $b(a,b){return a[0]==b[0]&&a[2]==b[2]&&a[1]==b[1]&&a[3]==b[3]}function ac(a,b){b[0]<a[0]&&(a[0]=b[0]);b[2]>a[2]&&(a[2]=b[2]);b[1]<a[1]&&(a[1]=b[1]);b[3]>a[3]&&(a[3]=b[3]);return a}function Mb(a,b){b[0]<a[0]&&(a[0]=b[0]);b[0]>a[2]&&(a[2]=b[0]);b[1]<a[1]&&(a[1]=b[1]);b[1]>a[3]&&(a[3]=b[1])}function Zb(a,b,c,d,e){for(;c<d;c+=e){var f=a,g=b[c],h=b[c+1];f[0]=Math.min(f[0],g);f[1]=Math.min(f[1],h);f[2]=Math.max(f[2],g);f[3]=Math.max(f[3],h)}return a} +function bc(a,b,c){var d;return(d=b.call(c,cc(a)))||(d=b.call(c,dc(a)))||(d=b.call(c,ec(a)))?d:(d=b.call(c,fc(a)))?d:!1}function gc(a){var b=0;hc(a)||(b=ic(a)*jc(a));return b}function cc(a){return[a[0],a[1]]}function dc(a){return[a[2],a[1]]}function kc(a){return[(a[0]+a[2])/2,(a[1]+a[3])/2]} +function lc(a,b,c,d,e){var f=b*d[0]/2;d=b*d[1]/2;b=Math.cos(c);var g=Math.sin(c);c=f*b;f*=g;b*=d;var h=d*g,l=a[0],m=a[1];a=l-c+h;d=l-c-h;g=l+c-h;c=l+c+h;var h=m-f-b,l=m-f+b,n=m+f+b,f=m+f-b;return Wb(Math.min(a,d,g,c),Math.min(h,l,n,f),Math.max(a,d,g,c),Math.max(h,l,n,f),e)}function jc(a){return a[3]-a[1]}function mc(a,b,c){c=c?c:Lb();nc(a,b)&&(c[0]=a[0]>b[0]?a[0]:b[0],c[1]=a[1]>b[1]?a[1]:b[1],c[2]=a[2]<b[2]?a[2]:b[2],c[3]=a[3]<b[3]?a[3]:b[3]);return c}function fc(a){return[a[0],a[3]]} +function ec(a){return[a[2],a[3]]}function ic(a){return a[2]-a[0]}function nc(a,b){return a[0]<=b[2]&&a[2]>=b[0]&&a[1]<=b[3]&&a[3]>=b[1]}function hc(a){return a[2]<a[0]||a[3]<a[1]}function oc(a,b){var c=(a[2]-a[0])/2*(b-1),d=(a[3]-a[1])/2*(b-1);a[0]-=c;a[2]+=c;a[1]-=d;a[3]+=d} +function pc(a,b,c){a=[a[0],a[1],a[0],a[3],a[2],a[1],a[2],a[3]];b(a,a,2);var d=[a[0],a[2],a[4],a[6]],e=[a[1],a[3],a[5],a[7]];b=Math.min.apply(null,d);a=Math.min.apply(null,e);d=Math.max.apply(null,d);e=Math.max.apply(null,e);return Wb(b,a,d,e,c)};function qc(){return!0}function rc(){return!1};/* + + Latitude/longitude spherical geodesy formulae taken from + http://www.movable-type.co.uk/scripts/latlong.html + Licensed under CC-BY-3.0. +*/ +function sc(a){this.radius=a}sc.prototype.a=function(a){for(var b=0,c=a.length,d=a[c-1][0],e=a[c-1][1],f=0;f<c;f++)var g=a[f][0],h=a[f][1],b=b+wa(g-d)*(2+Math.sin(wa(e))+Math.sin(wa(h))),d=g,e=h;return b*this.radius*this.radius/2};sc.prototype.b=function(a,b){var c=wa(a[1]),d=wa(b[1]),e=(d-c)/2,f=wa(b[0]-a[0])/2,c=Math.sin(e)*Math.sin(e)+Math.sin(f)*Math.sin(f)*Math.cos(c)*Math.cos(d);return 2*this.radius*Math.atan2(Math.sqrt(c),Math.sqrt(1-c))}; +sc.prototype.offset=function(a,b,c){var d=wa(a[1]);b/=this.radius;var e=Math.asin(Math.sin(d)*Math.cos(b)+Math.cos(d)*Math.sin(b)*Math.cos(c));return[180*(wa(a[0])+Math.atan2(Math.sin(c)*Math.sin(b)*Math.cos(d),Math.cos(b)-Math.sin(d)*Math.sin(e)))/Math.PI,180*e/Math.PI]};var tc=new sc(6370997);var uc={};uc.degrees=2*Math.PI*tc.radius/360;uc.ft=.3048;uc.m=1;uc["us-ft"]=1200/3937; +function vc(a){this.cb=a.code;this.c=a.units;this.f=void 0!==a.extent?a.extent:null;this.i=void 0!==a.worldExtent?a.worldExtent:null;this.b=void 0!==a.axisOrientation?a.axisOrientation:"enu";this.g=void 0!==a.global?a.global:!1;this.a=!(!this.g||!this.f);this.o=void 0!==a.getPointResolution?a.getPointResolution:this.sk;this.l=null;this.j=a.metersPerUnit;var b=wc,c=a.code,d=xc||pa.proj4;if("function"==typeof d&&void 0===b[c]){var e=d.defs(c);if(void 0!==e){void 0!==e.axis&&void 0===a.axisOrientation&& +(this.b=e.axis);void 0===a.metersPerUnit&&(this.j=e.to_meter);void 0===a.units&&(this.c=e.units);for(var f in b)b=d.defs(f),void 0!==b&&(a=yc(f),b===e?zc([a,this]):(b=d(f,c),Ac(a,this,b.forward,b.inverse)))}}}k=vc.prototype;k.Sj=function(){return this.cb};k.H=function(){return this.f};k.wb=function(){return this.c};k.$b=function(){return this.j||uc[this.c]};k.Ek=function(){return this.i};k.pl=function(){return this.g};k.ep=function(a){this.g=a;this.a=!(!a||!this.f)}; +k.Mm=function(a){this.f=a;this.a=!(!this.g||!a)};k.mp=function(a){this.i=a};k.cp=function(a){this.o=a};k.sk=function(a,b){if("degrees"==this.wb())return a;var c=Bc(this,yc("EPSG:4326")),d=[b[0]-a/2,b[1],b[0]+a/2,b[1],b[0],b[1]-a/2,b[0],b[1]+a/2],d=c(d,d,2),c=tc.b(d.slice(0,2),d.slice(2,4)),d=tc.b(d.slice(4,6),d.slice(6,8)),d=(c+d)/2,c=this.$b();void 0!==c&&(d/=c);return d};k.getPointResolution=function(a,b){return this.o(a,b)};var wc={},Cc={},xc=null; +function zc(a){Dc(a);a.forEach(function(b){a.forEach(function(a){b!==a&&Ec(b,a,Fc)})})}function Gc(){var a=Hc,b=Ic,c=Jc;Kc.forEach(function(d){a.forEach(function(a){Ec(d,a,b);Ec(a,d,c)})})}function Lc(a){wc[a.cb]=a;Ec(a,a,Fc)}function Dc(a){var b=[];a.forEach(function(a){b.push(Lc(a))})}function Mc(a){return a?"string"===typeof a?yc(a):a:yc("EPSG:3857")}function Ec(a,b,c){a=a.cb;b=b.cb;a in Cc||(Cc[a]={});Cc[a][b]=c}function Ac(a,b,c,d){a=yc(a);b=yc(b);Ec(a,b,Nc(c));Ec(b,a,Nc(d))} +function Nc(a){return function(b,c,d){var e=b.length;d=void 0!==d?d:2;c=void 0!==c?c:Array(e);var f,g;for(g=0;g<e;g+=d)for(f=a([b[g],b[g+1]]),c[g]=f[0],c[g+1]=f[1],f=d-1;2<=f;--f)c[g+f]=b[g+f];return c}}function yc(a){var b;if(a instanceof vc)b=a;else if("string"===typeof a){b=wc[a];var c=xc||pa.proj4;void 0===b&&"function"==typeof c&&void 0!==c.defs(a)&&(b=new vc({code:a}),Lc(b))}else b=null;return b}function Oc(a,b){if(a===b)return!0;var c=a.wb()===b.wb();return a.cb===b.cb?c:Bc(a,b)===Fc&&c} +function Pc(a,b){var c=yc(a),d=yc(b);return Bc(c,d)}function Bc(a,b){var c=a.cb,d=b.cb,e;c in Cc&&d in Cc[c]&&(e=Cc[c][d]);void 0===e&&(e=Qc);return e}function Qc(a,b){if(void 0!==b&&a!==b){for(var c=0,d=a.length;c<d;++c)b[c]=a[c];a=b}return a}function Fc(a,b){var c;if(void 0!==b){c=0;for(var d=a.length;c<d;++c)b[c]=a[c];c=b}else c=a.slice();return c}function Rc(a,b,c){return Pc(b,c)(a,void 0,a.length)}function Sc(a,b,c){b=Pc(b,c);return pc(a,b)};function Tc(){eb.call(this);this.v=Lb();this.A=-1;this.l={};this.s=this.o=0}y(Tc,eb);k=Tc.prototype;k.vb=function(a,b){var c=b?b:[NaN,NaN];this.sb(a[0],a[1],c,Infinity);return c};k.sg=function(a){return this.Bc(a[0],a[1])};k.Bc=rc;k.H=function(a){this.A!=this.g&&(this.v=this.Od(this.v),this.A=this.g);var b=this.v;a?(a[0]=b[0],a[1]=b[1],a[2]=b[2],a[3]=b[3]):a=b;return a};k.Bb=function(a){return this.od(a*a)};k.jb=function(a,b){this.rc(Pc(a,b));return this};function Uc(a){this.length=a.length||a;for(var b=0;b<this.length;b++)this[b]=a[b]||0}Uc.prototype.BYTES_PER_ELEMENT=4;Uc.prototype.set=function(a,b){b=b||0;for(var c=0;c<a.length&&b+c<this.length;c++)this[b+c]=a[c]};Uc.prototype.toString=Array.prototype.join;"undefined"==typeof Float32Array&&(Uc.BYTES_PER_ELEMENT=4,Uc.prototype.BYTES_PER_ELEMENT=Uc.prototype.BYTES_PER_ELEMENT,Uc.prototype.set=Uc.prototype.set,Uc.prototype.toString=Uc.prototype.toString,t("Float32Array",Uc,void 0));function Vc(a){this.length=a.length||a;for(var b=0;b<this.length;b++)this[b]=a[b]||0}Vc.prototype.BYTES_PER_ELEMENT=8;Vc.prototype.set=function(a,b){b=b||0;for(var c=0;c<a.length&&b+c<this.length;c++)this[b+c]=a[c]};Vc.prototype.toString=Array.prototype.join;if("undefined"==typeof Float64Array){try{Vc.BYTES_PER_ELEMENT=8}catch(a){}Vc.prototype.BYTES_PER_ELEMENT=Vc.prototype.BYTES_PER_ELEMENT;Vc.prototype.set=Vc.prototype.set;Vc.prototype.toString=Vc.prototype.toString;t("Float64Array",Vc,void 0)};function Wc(a,b,c,d,e){a[0]=b;a[1]=c;a[2]=d;a[3]=e};function Xc(){var a=Array(16);Yc(a,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);return a}function Zc(){var a=Array(16);Yc(a,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1);return a}function Yc(a,b,c,d,e,f,g,h,l,m,n,p,q,r,u,x,v){a[0]=b;a[1]=c;a[2]=d;a[3]=e;a[4]=f;a[5]=g;a[6]=h;a[7]=l;a[8]=m;a[9]=n;a[10]=p;a[11]=q;a[12]=r;a[13]=u;a[14]=x;a[15]=v} +function $c(a,b){a[0]=b[0];a[1]=b[1];a[2]=b[2];a[3]=b[3];a[4]=b[4];a[5]=b[5];a[6]=b[6];a[7]=b[7];a[8]=b[8];a[9]=b[9];a[10]=b[10];a[11]=b[11];a[12]=b[12];a[13]=b[13];a[14]=b[14];a[15]=b[15]}function ad(a){a[0]=1;a[1]=0;a[2]=0;a[3]=0;a[4]=0;a[5]=1;a[6]=0;a[7]=0;a[8]=0;a[9]=0;a[10]=1;a[11]=0;a[12]=0;a[13]=0;a[14]=0;a[15]=1} +function bd(a,b,c){var d=a[0],e=a[1],f=a[2],g=a[3],h=a[4],l=a[5],m=a[6],n=a[7],p=a[8],q=a[9],r=a[10],u=a[11],x=a[12],v=a[13],D=a[14];a=a[15];var A=b[0],z=b[1],F=b[2],N=b[3],K=b[4],X=b[5],oa=b[6],H=b[7],ya=b[8],Ua=b[9],Xa=b[10],Va=b[11],Aa=b[12],Qb=b[13],Nb=b[14];b=b[15];c[0]=d*A+h*z+p*F+x*N;c[1]=e*A+l*z+q*F+v*N;c[2]=f*A+m*z+r*F+D*N;c[3]=g*A+n*z+u*F+a*N;c[4]=d*K+h*X+p*oa+x*H;c[5]=e*K+l*X+q*oa+v*H;c[6]=f*K+m*X+r*oa+D*H;c[7]=g*K+n*X+u*oa+a*H;c[8]=d*ya+h*Ua+p*Xa+x*Va;c[9]=e*ya+l*Ua+q*Xa+v*Va;c[10]=f* +ya+m*Ua+r*Xa+D*Va;c[11]=g*ya+n*Ua+u*Xa+a*Va;c[12]=d*Aa+h*Qb+p*Nb+x*b;c[13]=e*Aa+l*Qb+q*Nb+v*b;c[14]=f*Aa+m*Qb+r*Nb+D*b;c[15]=g*Aa+n*Qb+u*Nb+a*b} +function cd(a,b){var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],l=a[6],m=a[7],n=a[8],p=a[9],q=a[10],r=a[11],u=a[12],x=a[13],v=a[14],D=a[15],A=c*h-d*g,z=c*l-e*g,F=c*m-f*g,N=d*l-e*h,K=d*m-f*h,X=e*m-f*l,oa=n*x-p*u,H=n*v-q*u,ya=n*D-r*u,Ua=p*v-q*x,Xa=p*D-r*x,Va=q*D-r*v,Aa=A*Va-z*Xa+F*Ua+N*ya-K*H+X*oa;0!=Aa&&(Aa=1/Aa,b[0]=(h*Va-l*Xa+m*Ua)*Aa,b[1]=(-d*Va+e*Xa-f*Ua)*Aa,b[2]=(x*X-v*K+D*N)*Aa,b[3]=(-p*X+q*K-r*N)*Aa,b[4]=(-g*Va+l*ya-m*H)*Aa,b[5]=(c*Va-e*ya+f*H)*Aa,b[6]=(-u*X+v*F-D*z)*Aa,b[7]=(n*X-q*F+r*z)*Aa, +b[8]=(g*Xa-h*ya+m*oa)*Aa,b[9]=(-c*Xa+d*ya-f*oa)*Aa,b[10]=(u*K-x*F+D*A)*Aa,b[11]=(-n*K+p*F-r*A)*Aa,b[12]=(-g*Ua+h*H-l*oa)*Aa,b[13]=(c*Ua-d*H+e*oa)*Aa,b[14]=(-u*N+x*z-v*A)*Aa,b[15]=(n*N-p*z+q*A)*Aa)}function dd(a,b,c){var d=a[1]*b+a[5]*c+0*a[9]+a[13],e=a[2]*b+a[6]*c+0*a[10]+a[14],f=a[3]*b+a[7]*c+0*a[11]+a[15];a[12]=a[0]*b+a[4]*c+0*a[8]+a[12];a[13]=d;a[14]=e;a[15]=f} +function ed(a,b,c){Yc(a,a[0]*b,a[1]*b,a[2]*b,a[3]*b,a[4]*c,a[5]*c,a[6]*c,a[7]*c,1*a[8],1*a[9],1*a[10],1*a[11],a[12],a[13],a[14],a[15])}function fd(a,b){var c=a[0],d=a[1],e=a[2],f=a[3],g=a[4],h=a[5],l=a[6],m=a[7],n=Math.cos(b),p=Math.sin(b);a[0]=c*n+g*p;a[1]=d*n+h*p;a[2]=e*n+l*p;a[3]=f*n+m*p;a[4]=c*-p+g*n;a[5]=d*-p+h*n;a[6]=e*-p+l*n;a[7]=f*-p+m*n}new Float64Array(3);new Float64Array(3);new Float64Array(4);new Float64Array(4);new Float64Array(4);new Float64Array(16);function gd(a,b,c,d,e,f){var g=e[0],h=e[1],l=e[4],m=e[5],n=e[12];e=e[13];for(var p=f?f:[],q=0;b<c;b+=d){var r=a[b],u=a[b+1];p[q++]=g*r+l*u+n;p[q++]=h*r+m*u+e}f&&p.length!=q&&(p.length=q);return p};function hd(){Tc.call(this);this.f="XY";this.a=2;this.B=null}y(hd,Tc);function id(a){if("XY"==a)return 2;if("XYZ"==a||"XYM"==a)return 3;if("XYZM"==a)return 4}k=hd.prototype;k.Bc=rc;k.Od=function(a){return Yb(this.B,0,this.B.length,this.a,a)};k.Ib=function(){return this.B.slice(0,this.a)};k.la=function(){return this.B};k.Jb=function(){return this.B.slice(this.B.length-this.a)};k.Kb=function(){return this.f}; +k.od=function(a){this.s!=this.g&&(Fa(this.l),this.o=0,this.s=this.g);if(0>a||0!==this.o&&a<=this.o)return this;var b=a.toString();if(this.l.hasOwnProperty(b))return this.l[b];var c=this.Nc(a);if(c.la().length<this.B.length)return this.l[b]=c;this.o=a;return this};k.Nc=function(){return this};k.va=function(){return this.a};function jd(a,b,c){a.a=id(b);a.f=b;a.B=c} +function kd(a,b,c,d){if(b)c=id(b);else{for(b=0;b<d;++b){if(0===c.length){a.f="XY";a.a=2;return}c=c[0]}c=c.length;b=2==c?"XY":3==c?"XYZ":4==c?"XYZM":void 0}a.f=b;a.a=c}k.rc=function(a){this.B&&(a(this.B,this.B,this.a),this.u())};k.rotate=function(a,b){var c=this.la();if(c){for(var d=c.length,e=this.va(),f=c?c:[],g=Math.cos(a),h=Math.sin(a),l=b[0],m=b[1],n=0,p=0;p<d;p+=e){var q=c[p]-l,r=c[p+1]-m;f[n++]=l+q*g-r*h;f[n++]=m+q*h+r*g;for(q=p+2;q<p+e;++q)f[n++]=c[q]}c&&f.length!=n&&(f.length=n);this.u()}}; +k.Sc=function(a,b){var c=this.la();if(c){var d=c.length,e=this.va(),f=c?c:[],g=0,h,l;for(h=0;h<d;h+=e)for(f[g++]=c[h]+a,f[g++]=c[h+1]+b,l=h+2;l<h+e;++l)f[g++]=c[l];c&&f.length!=g&&(f.length=g);this.u()}};function ld(a,b,c,d){for(var e=0,f=a[c-d],g=a[c-d+1];b<c;b+=d)var h=a[b],l=a[b+1],e=e+(g*h-f*l),f=h,g=l;return e/2}function md(a,b,c,d){var e=0,f,g;f=0;for(g=c.length;f<g;++f){var h=c[f],e=e+ld(a,b,h,d);b=h}return e};function nd(a,b,c,d,e,f,g){var h=a[b],l=a[b+1],m=a[c]-h,n=a[c+1]-l;if(0!==m||0!==n)if(f=((e-h)*m+(f-l)*n)/(m*m+n*n),1<f)b=c;else if(0<f){for(e=0;e<d;++e)g[e]=za(a[b+e],a[c+e],f);g.length=d;return}for(e=0;e<d;++e)g[e]=a[b+e];g.length=d}function od(a,b,c,d,e){var f=a[b],g=a[b+1];for(b+=d;b<c;b+=d){var h=a[b],l=a[b+1],f=va(f,g,h,l);f>e&&(e=f);f=h;g=l}return e}function pd(a,b,c,d,e){var f,g;f=0;for(g=c.length;f<g;++f){var h=c[f];e=od(a,b,h,d,e);b=h}return e} +function qd(a,b,c,d,e,f,g,h,l,m,n){if(b==c)return m;var p;if(0===e){p=va(g,h,a[b],a[b+1]);if(p<m){for(n=0;n<d;++n)l[n]=a[b+n];l.length=d;return p}return m}for(var q=n?n:[NaN,NaN],r=b+d;r<c;)if(nd(a,r-d,r,d,g,h,q),p=va(g,h,q[0],q[1]),p<m){m=p;for(n=0;n<d;++n)l[n]=q[n];l.length=d;r+=d}else r+=d*Math.max((Math.sqrt(p)-Math.sqrt(m))/e|0,1);if(f&&(nd(a,c-d,b,d,g,h,q),p=va(g,h,q[0],q[1]),p<m)){m=p;for(n=0;n<d;++n)l[n]=q[n];l.length=d}return m} +function rd(a,b,c,d,e,f,g,h,l,m,n){n=n?n:[NaN,NaN];var p,q;p=0;for(q=c.length;p<q;++p){var r=c[p];m=qd(a,b,r,d,e,f,g,h,l,m,n);b=r}return m};function sd(a,b){var c=0,d,e;d=0;for(e=b.length;d<e;++d)a[c++]=b[d];return c}function td(a,b,c,d){var e,f;e=0;for(f=c.length;e<f;++e){var g=c[e],h;for(h=0;h<d;++h)a[b++]=g[h]}return b}function ud(a,b,c,d,e){e=e?e:[];var f=0,g,h;g=0;for(h=c.length;g<h;++g)b=td(a,b,c[g],d),e[f++]=b;e.length=f;return e};function vd(a,b,c,d,e){e=void 0!==e?e:[];for(var f=0;b<c;b+=d)e[f++]=a.slice(b,b+d);e.length=f;return e}function wd(a,b,c,d,e){e=void 0!==e?e:[];var f=0,g,h;g=0;for(h=c.length;g<h;++g){var l=c[g];e[f++]=vd(a,b,l,d,e[f]);b=l}e.length=f;return e};function xd(a,b,c,d,e,f,g){var h=(c-b)/d;if(3>h){for(;b<c;b+=d)f[g++]=a[b],f[g++]=a[b+1];return g}var l=Array(h);l[0]=1;l[h-1]=1;c=[b,c-d];for(var m=0,n;0<c.length;){var p=c.pop(),q=c.pop(),r=0,u=a[q],x=a[q+1],v=a[p],D=a[p+1];for(n=q+d;n<p;n+=d){var A=ua(a[n],a[n+1],u,x,v,D);A>r&&(m=n,r=A)}r>e&&(l[(m-b)/d]=1,q+d<m&&c.push(q,m),m+d<p&&c.push(m,p))}for(n=0;n<h;++n)l[n]&&(f[g++]=a[b+n*d],f[g++]=a[b+n*d+1]);return g} +function yd(a,b,c,d,e,f,g,h){var l,m;l=0;for(m=c.length;l<m;++l){var n=c[l];a:{var p=a,q=n,r=d,u=e,x=f;if(b!=q){var v=u*Math.round(p[b]/u),D=u*Math.round(p[b+1]/u);b+=r;x[g++]=v;x[g++]=D;var A,z;do if(A=u*Math.round(p[b]/u),z=u*Math.round(p[b+1]/u),b+=r,b==q){x[g++]=A;x[g++]=z;break a}while(A==v&&z==D);for(;b<q;){var F,N;F=u*Math.round(p[b]/u);N=u*Math.round(p[b+1]/u);b+=r;if(F!=A||N!=z){var K=A-v,X=z-D,oa=F-v,H=N-D;K*H==X*oa&&(0>K&&oa<K||K==oa||0<K&&oa>K)&&(0>X&&H<X||X==H||0<X&&H>X)||(x[g++]=A,x[g++]= +z,v=A,D=z);A=F;z=N}}x[g++]=A;x[g++]=z}}h.push(g);b=n}return g};function zd(a,b){hd.call(this);this.i=this.j=-1;this.pa(a,b)}y(zd,hd);k=zd.prototype;k.clone=function(){var a=new zd(null);Ad(a,this.f,this.B.slice());return a};k.sb=function(a,b,c,d){if(d<Rb(this.H(),a,b))return d;this.i!=this.g&&(this.j=Math.sqrt(od(this.B,0,this.B.length,this.a,0)),this.i=this.g);return qd(this.B,0,this.B.length,this.a,this.j,!0,a,b,c,d)};k.nm=function(){return ld(this.B,0,this.B.length,this.a)};k.Z=function(){return vd(this.B,0,this.B.length,this.a)}; +k.Nc=function(a){var b=[];b.length=xd(this.B,0,this.B.length,this.a,a,b,0);a=new zd(null);Ad(a,"XY",b);return a};k.X=function(){return"LinearRing"};k.pa=function(a,b){a?(kd(this,b,a,1),this.B||(this.B=[]),this.B.length=td(this.B,0,a,this.a),this.u()):Ad(this,"XY",null)};function Ad(a,b,c){jd(a,b,c);a.u()};function C(a,b){hd.call(this);this.pa(a,b)}y(C,hd);k=C.prototype;k.clone=function(){var a=new C(null);a.ba(this.f,this.B.slice());return a};k.sb=function(a,b,c,d){var e=this.B;a=va(a,b,e[0],e[1]);if(a<d){d=this.a;for(b=0;b<d;++b)c[b]=e[b];c.length=d;return a}return d};k.Z=function(){return this.B?this.B.slice():[]};k.Od=function(a){return Xb(this.B,a)};k.X=function(){return"Point"};k.Ka=function(a){return Tb(a,this.B[0],this.B[1])}; +k.pa=function(a,b){a?(kd(this,b,a,0),this.B||(this.B=[]),this.B.length=sd(this.B,a),this.u()):this.ba("XY",null)};k.ba=function(a,b){jd(this,a,b);this.u()};function Bd(a,b,c,d,e){return!bc(e,function(e){return!Cd(a,b,c,d,e[0],e[1])})}function Cd(a,b,c,d,e,f){for(var g=!1,h=a[c-d],l=a[c-d+1];b<c;b+=d){var m=a[b],n=a[b+1];l>f!=n>f&&e<(m-h)*(f-l)/(n-l)+h&&(g=!g);h=m;l=n}return g}function Dd(a,b,c,d,e,f){if(0===c.length||!Cd(a,b,c[0],d,e,f))return!1;var g;b=1;for(g=c.length;b<g;++b)if(Cd(a,c[b-1],c[b],d,e,f))return!1;return!0};function Ed(a,b,c,d,e,f,g){var h,l,m,n,p,q=e[f+1],r=[],u=c[0];m=a[u-d];p=a[u-d+1];for(h=b;h<u;h+=d){n=a[h];l=a[h+1];if(q<=p&&l<=q||p<=q&&q<=l)m=(q-p)/(l-p)*(n-m)+m,r.push(m);m=n;p=l}u=NaN;p=-Infinity;r.sort(ib);m=r[0];h=1;for(l=r.length;h<l;++h){n=r[h];var x=Math.abs(n-m);x>p&&(m=(m+n)/2,Dd(a,b,c,d,m,q)&&(u=m,p=x));m=n}isNaN(u)&&(u=e[f]);return g?(g.push(u,q),g):[u,q]};function Fd(a,b,c,d,e,f){for(var g=[a[b],a[b+1]],h=[],l;b+d<c;b+=d){h[0]=a[b+d];h[1]=a[b+d+1];if(l=e.call(f,g,h))return l;g[0]=h[0];g[1]=h[1]}return!1};function Gd(a,b,c,d,e){var f=Zb(Lb(),a,b,c,d);return nc(e,f)?Ub(e,f)||f[0]>=e[0]&&f[2]<=e[2]||f[1]>=e[1]&&f[3]<=e[3]?!0:Fd(a,b,c,d,function(a,b){var c=!1,d=Vb(e,a),f=Vb(e,b);if(1===d||1===f)c=!0;else{var p=e[0],q=e[1],r=e[2],u=e[3],x=b[0],v=b[1],D=(v-a[1])/(x-a[0]);f&2&&!(d&2)&&(c=x-(v-u)/D,c=c>=p&&c<=r);c||!(f&4)||d&4||(c=v-(x-r)*D,c=c>=q&&c<=u);c||!(f&8)||d&8||(c=x-(v-q)/D,c=c>=p&&c<=r);c||!(f&16)||d&16||(c=v-(x-p)*D,c=c>=q&&c<=u)}return c}):!1} +function Hd(a,b,c,d,e){var f=c[0];if(!(Gd(a,b,f,d,e)||Cd(a,b,f,d,e[0],e[1])||Cd(a,b,f,d,e[0],e[3])||Cd(a,b,f,d,e[2],e[1])||Cd(a,b,f,d,e[2],e[3])))return!1;if(1===c.length)return!0;b=1;for(f=c.length;b<f;++b)if(Bd(a,c[b-1],c[b],d,e))return!1;return!0};function Id(a,b,c,d){for(var e=0,f=a[c-d],g=a[c-d+1];b<c;b+=d)var h=a[b],l=a[b+1],e=e+(h-f)*(l+g),f=h,g=l;return 0<e}function Jd(a,b,c,d){var e=0;d=void 0!==d?d:!1;var f,g;f=0;for(g=b.length;f<g;++f){var h=b[f],e=Id(a,e,h,c);if(0===f){if(d&&e||!d&&!e)return!1}else if(d&&!e||!d&&e)return!1;e=h}return!0} +function Kd(a,b,c,d,e){e=void 0!==e?e:!1;var f,g;f=0;for(g=c.length;f<g;++f){var h=c[f],l=Id(a,b,h,d);if(0===f?e&&l||!e&&!l:e&&!l||!e&&l)for(var l=a,m=h,n=d;b<m-n;){var p;for(p=0;p<n;++p){var q=l[b+p];l[b+p]=l[m-n+p];l[m-n+p]=q}b+=n;m-=n}b=h}return b}function Ld(a,b,c,d){var e=0,f,g;f=0;for(g=b.length;f<g;++f)e=Kd(a,e,b[f],c,d);return e};function E(a,b){hd.call(this);this.i=[];this.C=-1;this.D=null;this.T=this.R=this.S=-1;this.j=null;this.pa(a,b)}y(E,hd);k=E.prototype;k.yj=function(a){this.B?mb(this.B,a.la()):this.B=a.la().slice();this.i.push(this.B.length);this.u()};k.clone=function(){var a=new E(null);a.ba(this.f,this.B.slice(),this.i.slice());return a}; +k.sb=function(a,b,c,d){if(d<Rb(this.H(),a,b))return d;this.R!=this.g&&(this.S=Math.sqrt(pd(this.B,0,this.i,this.a,0)),this.R=this.g);return rd(this.B,0,this.i,this.a,this.S,!0,a,b,c,d)};k.Bc=function(a,b){return Dd(this.Mb(),0,this.i,this.a,a,b)};k.qm=function(){return md(this.Mb(),0,this.i,this.a)};k.Z=function(a){var b;void 0!==a?(b=this.Mb().slice(),Kd(b,0,this.i,this.a,a)):b=this.B;return wd(b,0,this.i,this.a)};k.Db=function(){return this.i}; +function Md(a){if(a.C!=a.g){var b=kc(a.H());a.D=Ed(a.Mb(),0,a.i,a.a,b,0);a.C=a.g}return a.D}k.bk=function(){return new C(Md(this))};k.gk=function(){return this.i.length};k.Hg=function(a){if(0>a||this.i.length<=a)return null;var b=new zd(null);Ad(b,this.f,this.B.slice(0===a?0:this.i[a-1],this.i[a]));return b};k.Vd=function(){var a=this.f,b=this.B,c=this.i,d=[],e=0,f,g;f=0;for(g=c.length;f<g;++f){var h=c[f],l=new zd(null);Ad(l,a,b.slice(e,h));d.push(l);e=h}return d}; +k.Mb=function(){if(this.T!=this.g){var a=this.B;Jd(a,this.i,this.a)?this.j=a:(this.j=a.slice(),this.j.length=Kd(this.j,0,this.i,this.a));this.T=this.g}return this.j};k.Nc=function(a){var b=[],c=[];b.length=yd(this.B,0,this.i,this.a,Math.sqrt(a),b,0,c);a=new E(null);a.ba("XY",b,c);return a};k.X=function(){return"Polygon"};k.Ka=function(a){return Hd(this.Mb(),0,this.i,this.a,a)}; +k.pa=function(a,b){if(a){kd(this,b,a,2);this.B||(this.B=[]);var c=ud(this.B,0,a,this.a,this.i);this.B.length=0===c.length?0:c[c.length-1];this.u()}else this.ba("XY",null,this.i)};k.ba=function(a,b,c){jd(this,a,b);this.i=c;this.u()};function Nd(a,b,c,d){var e=d?d:32;d=[];var f;for(f=0;f<e;++f)mb(d,a.offset(b,c,2*Math.PI*f/e));d.push(d[0],d[1]);a=new E(null);a.ba("XY",d,[d.length]);return a} +function Od(a){var b=a[0],c=a[1],d=a[2];a=a[3];b=[b,c,b,a,d,a,d,c,b,c];c=new E(null);c.ba("XY",b,[b.length]);return c}function Pd(a,b,c){var d=b?b:32,e=a.va();b=a.f;for(var f=new E(null,b),d=e*(d+1),e=Array(d),g=0;g<d;g++)e[g]=0;f.ba(b,e,[e.length]);Qd(f,a.rd(),a.wf(),c);return f}function Qd(a,b,c,d){var e=a.la(),f=a.f,g=a.va(),h=a.Db(),l=e.length/g-1;d=d?d:0;for(var m,n,p=0;p<=l;++p)n=p*g,m=d+2*xa(p,l)*Math.PI/l,e[n]=b[0]+c*Math.cos(m),e[n+1]=b[1]+c*Math.sin(m);a.ba(f,e,h)};function Rd(a){eb.call(this);a=a||{};this.f=[0,0];var b={};b.center=void 0!==a.center?a.center:null;this.l=Mc(a.projection);var c,d,e,f=void 0!==a.minZoom?a.minZoom:0;c=void 0!==a.maxZoom?a.maxZoom:28;var g=void 0!==a.zoomFactor?a.zoomFactor:2;if(void 0!==a.resolutions)c=a.resolutions,d=c[0],e=c[c.length-1],c=tb(c);else{d=Mc(a.projection);e=d.H();var h=(e?Math.max(ic(e),jc(e)):360*uc.degrees/d.$b())/256/Math.pow(2,0),l=h/Math.pow(2,28);d=a.maxResolution;void 0!==d?f=0:d=h/Math.pow(g,f);e=a.minResolution; +void 0===e&&(e=void 0!==a.maxZoom?void 0!==a.maxResolution?d/Math.pow(g,c):h/Math.pow(g,c):l);c=f+Math.floor(Math.log(d/e)/Math.log(g));e=d/Math.pow(g,c-f);c=ub(g,d,c-f)}this.a=d;this.c=e;this.j=a.resolutions;this.i=f;f=void 0!==a.extent?Ba(a.extent):Ca;(void 0!==a.enableRotation?a.enableRotation:1)?(d=a.constrainRotation,d=void 0===d||!0===d?yb():!1===d?wb:ea(d)?xb(d):wb):d=vb;this.o=new Da(f,c,d);void 0!==a.resolution?b.resolution=a.resolution:void 0!==a.zoom&&(b.resolution=this.constrainResolution(this.a, +a.zoom-this.i));b.rotation=void 0!==a.rotation?a.rotation:0;this.G(b)}y(Rd,eb);k=Rd.prototype;k.Pd=function(a){return this.o.center(a)};k.constrainResolution=function(a,b,c){return this.o.resolution(a,b||0,c||0)};k.constrainRotation=function(a,b){return this.o.rotation(a,b||0)};k.ab=function(){return this.get("center")};function Sd(a,b){return void 0!==b?(b[0]=a.f[0],b[1]=a.f[1],b):a.f.slice()}k.Kc=function(a){var b=this.ab(),c=this.$(),d=this.La();return lc(b,c,d,a)};k.Vl=function(){return this.a}; +k.Wl=function(){return this.c};k.Xl=function(){return this.l};k.$=function(){return this.get("resolution")};k.Yl=function(){return this.j};function Td(a,b){return Math.max(ic(a)/b[0],jc(a)/b[1])}function Ud(a){var b=a.a,c=Math.log(b/a.c)/Math.log(2);return function(a){return b/Math.pow(2,a*c)}}k.La=function(){return this.get("rotation")};function Vd(a){var b=a.a,c=Math.log(b/a.c)/Math.log(2);return function(a){return Math.log(b/a)/Math.log(2)/c}} +k.V=function(){var a=this.ab(),b=this.l,c=this.$(),d=this.La();return{center:[Math.round(a[0]/c)*c,Math.round(a[1]/c)*c],projection:void 0!==b?b:null,resolution:c,rotation:d}};k.Fk=function(){var a,b=this.$();if(void 0!==b){var c,d=0;do{c=this.constrainResolution(this.a,d);if(c==b){a=d;break}++d}while(c>this.c)}return void 0!==a?this.i+a:a}; +k.cf=function(a,b,c){a instanceof hd||(a=Od(a));var d=c||{};c=void 0!==d.padding?d.padding:[0,0,0,0];var e=void 0!==d.constrainResolution?d.constrainResolution:!0,f=void 0!==d.nearest?d.nearest:!1,g;void 0!==d.minResolution?g=d.minResolution:void 0!==d.maxZoom?g=this.constrainResolution(this.a,d.maxZoom-this.i,0):g=0;var h=a.la(),l=this.La(),d=Math.cos(-l),l=Math.sin(-l),m=Infinity,n=Infinity,p=-Infinity,q=-Infinity;a=a.va();for(var r=0,u=h.length;r<u;r+=a)var x=h[r]*d-h[r+1]*l,v=h[r]*l+h[r+1]*d, +m=Math.min(m,x),n=Math.min(n,v),p=Math.max(p,x),q=Math.max(q,v);b=Td([m,n,p,q],[b[0]-c[1]-c[3],b[1]-c[0]-c[2]]);b=isNaN(b)?g:Math.max(b,g);e&&(g=this.constrainResolution(b,0,0),!f&&g<b&&(g=this.constrainResolution(g,-1,0)),b=g);this.Ub(b);l=-l;f=(m+p)/2+(c[1]-c[3])/2*b;c=(n+q)/2+(c[0]-c[2])/2*b;this.mb([f*d-c*l,c*d+f*l])}; +k.Ej=function(a,b,c){var d=this.La(),e=Math.cos(-d),d=Math.sin(-d),f=a[0]*e-a[1]*d;a=a[1]*e+a[0]*d;var g=this.$(),f=f+(b[0]/2-c[0])*g;a+=(c[1]-b[1]/2)*g;d=-d;this.mb([f*e-a*d,a*e+f*d])};function Wd(a){return!!a.ab()&&void 0!==a.$()}k.rotate=function(a,b){if(void 0!==b){var c,d=this.ab();void 0!==d&&(c=[d[0]-b[0],d[1]-b[1]],Gb(c,a-this.La()),Bb(c,b));this.mb(c)}this.ie(a)};k.mb=function(a){this.set("center",a)};function Xd(a,b){a.f[1]+=b}k.Ub=function(a){this.set("resolution",a)}; +k.ie=function(a){this.set("rotation",a)};k.np=function(a){a=this.constrainResolution(this.a,a-this.i,0);this.Ub(a)};function Yd(a){return Math.pow(a,3)}function Zd(a){return 1-Yd(1-a)}function $d(a){return 3*a*a-2*a*a*a}function ae(a){return a}function be(a){return.5>a?$d(2*a):1-$d(2*(a-.5))};function ce(a){var b=a.source,c=a.start?a.start:Date.now(),d=b[0],e=b[1],f=void 0!==a.duration?a.duration:1E3,g=a.easing?a.easing:$d;return function(a,b){if(b.time<c)return b.animate=!0,b.viewHints[0]+=1,!0;if(b.time<c+f){var m=1-g((b.time-c)/f),n=d-b.viewState.center[0],p=e-b.viewState.center[1];b.animate=!0;b.viewState.center[0]+=m*n;b.viewState.center[1]+=m*p;b.viewHints[0]+=1;return!0}return!1}} +function de(a){var b=a.rotation?a.rotation:0,c=a.start?a.start:Date.now(),d=void 0!==a.duration?a.duration:1E3,e=a.easing?a.easing:$d,f=a.anchor?a.anchor:null;return function(a,h){if(h.time<c)return h.animate=!0,h.viewHints[0]+=1,!0;if(h.time<c+d){var l=1-e((h.time-c)/d),l=(b-h.viewState.rotation)*l;h.animate=!0;h.viewState.rotation+=l;if(f){var m=h.viewState.center;m[0]-=f[0];m[1]-=f[1];Gb(m,l);Bb(m,f)}h.viewHints[0]+=1;return!0}return!1}} +function ee(a){var b=a.resolution,c=a.start?a.start:Date.now(),d=void 0!==a.duration?a.duration:1E3,e=a.easing?a.easing:$d;return function(a,g){if(g.time<c)return g.animate=!0,g.viewHints[0]+=1,!0;if(g.time<c+d){var h=1-e((g.time-c)/d),l=b-g.viewState.resolution;g.animate=!0;g.viewState.resolution+=h*l;g.viewHints[0]+=1;return!0}return!1}};function fe(a,b,c,d){this.ca=a;this.ea=b;this.fa=c;this.ga=d}fe.prototype.contains=function(a){return ge(this,a[1],a[2])};function ge(a,b,c){return a.ca<=b&&b<=a.ea&&a.fa<=c&&c<=a.ga}function he(a,b){return a.ca==b.ca&&a.fa==b.fa&&a.ea==b.ea&&a.ga==b.ga}function ie(a,b){return a.ca<=b.ea&&a.ea>=b.ca&&a.fa<=b.ga&&a.ga>=b.fa};function je(a){this.a=a.html;this.b=a.tileRanges?a.tileRanges:null}je.prototype.g=function(){return this.a};function ke(a,b,c){Wa.call(this,a,c);this.element=b}y(ke,Wa);function le(a){eb.call(this);this.a=a?a:[];me(this)}y(le,eb);k=le.prototype;k.clear=function(){for(;0<this.dc();)this.pop()};k.qf=function(a){var b,c;b=0;for(c=a.length;b<c;++b)this.push(a[b]);return this};k.forEach=function(a,b){this.a.forEach(a,b)};k.Gl=function(){return this.a};k.item=function(a){return this.a[a]};k.dc=function(){return this.get("length")};k.ee=function(a,b){this.a.splice(a,0,b);me(this);this.b(new ke("add",b,this))}; +k.pop=function(){return this.Rf(this.dc()-1)};k.push=function(a){var b=this.a.length;this.ee(b,a);return b};k.remove=function(a){var b=this.a,c,d;c=0;for(d=b.length;c<d;++c)if(b[c]===a)return this.Rf(c)};k.Rf=function(a){var b=this.a[a];this.a.splice(a,1);me(this);this.b(new ke("remove",b,this));return b};k.Zo=function(a,b){var c=this.dc();if(a<c)c=this.a[a],this.a[a]=b,this.b(new ke("remove",c,this)),this.b(new ke("add",b,this));else{for(;c<a;++c)this.ee(c,void 0);this.ee(a,b)}}; +function me(a){a.set("length",a.a.length)};function ne(a){return Array.prototype.concat.apply(Array.prototype,arguments)}function oe(a){var b=a.length;if(0<b){for(var c=Array(b),d=0;d<b;d++)c[d]=a[d];return c}return[]}function pe(a,b,c){return 2>=arguments.length?Array.prototype.slice.call(a,b):Array.prototype.slice.call(a,b,c)};var qe=/^#(?:[0-9a-f]{3}){1,2}$/i,re=/^(?:rgb)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2})\)$/i,se=/^(?:rgba)?\((0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|[1-9]\d{0,2}),\s?(0|1|0\.\d{0,10})\)$/i;function te(a){return Array.isArray(a)?a:ue(a)}function ve(a){if("string"!==typeof a){var b=a[0];b!=(b|0)&&(b=b+.5|0);var c=a[1];c!=(c|0)&&(c=c+.5|0);var d=a[2];d!=(d|0)&&(d=d+.5|0);a="rgba("+b+","+c+","+d+","+(void 0===a[3]?1:a[3])+")"}return a} +var ue=function(){var a={},b=0;return function(c){var d;if(a.hasOwnProperty(c))d=a[c];else{if(1024<=b){d=0;for(var e in a)0===(d++&3)&&(delete a[e],--b)}var f,g;qe.exec(c)?(g=3==c.length-1?1:2,d=parseInt(c.substr(1+0*g,g),16),e=parseInt(c.substr(1+1*g,g),16),f=parseInt(c.substr(1+2*g,g),16),1==g&&(d=(d<<4)+d,e=(e<<4)+e,f=(f<<4)+f),d=[d,e,f,1]):(g=se.exec(c))?(d=Number(g[1]),e=Number(g[2]),f=Number(g[3]),g=Number(g[4]),d=[d,e,f,g],d=we(d,d)):(g=re.exec(c))?(d=Number(g[1]),e=Number(g[2]),f=Number(g[3]), +d=[d,e,f,1],d=we(d,d)):d=void 0;a[c]=d;++b}return d}}();function we(a,b){var c=b||[];c[0]=sa(a[0]+.5|0,0,255);c[1]=sa(a[1]+.5|0,0,255);c[2]=sa(a[2]+.5|0,0,255);c[3]=sa(a[3],0,1);return c};function xe(a){return"string"===typeof a||a instanceof CanvasPattern||a instanceof CanvasGradient?a:ve(a)};var ye;a:{var ze=aa.navigator;if(ze){var Ae=ze.userAgent;if(Ae){ye=Ae;break a}}ye=""}function Be(a){return-1!=ye.indexOf(a)};var Ce=Be("Opera"),Ee=Be("Trident")||Be("MSIE"),Fe=Be("Edge"),Ge=Be("Gecko")&&!(-1!=ye.toLowerCase().indexOf("webkit")&&!Be("Edge"))&&!(Be("Trident")||Be("MSIE"))&&!Be("Edge"),He=-1!=ye.toLowerCase().indexOf("webkit")&&!Be("Edge"),Ie; +a:{var Je="",Ke=function(){var a=ye;if(Ge)return/rv\:([^\);]+)(\)|;)/.exec(a);if(Fe)return/Edge\/([\d\.]+)/.exec(a);if(Ee)return/\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/.exec(a);if(He)return/WebKit\/(\S+)/.exec(a);if(Ce)return/(?:Version)[ \/]?(\S+)/.exec(a)}();Ke&&(Je=Ke?Ke[1]:"");if(Ee){var Le,Me=aa.document;Le=Me?Me.documentMode:void 0;if(null!=Le&&Le>parseFloat(Je)){Ie=String(Le);break a}}Ie=Je}var Ne={};function Oe(a,b){var c=document.createElement("CANVAS");a&&(c.width=a);b&&(c.height=b);return c.getContext("2d")} +var Pe=function(){var a;return function(){if(void 0===a){var b=document.createElement("P"),c,d={webkitTransform:"-webkit-transform",OTransform:"-o-transform",msTransform:"-ms-transform",MozTransform:"-moz-transform",transform:"transform"};document.body.appendChild(b);for(var e in d)e in b.style&&(b.style[e]="translate(1px,1px)",c=pa.getComputedStyle(b).getPropertyValue(d[e]));document.body.removeChild(b);a=c&&"none"!==c}return a}}(),Qe=function(){var a;return function(){if(void 0===a){var b=document.createElement("P"), +c,d={webkitTransform:"-webkit-transform",OTransform:"-o-transform",msTransform:"-ms-transform",MozTransform:"-moz-transform",transform:"transform"};document.body.appendChild(b);for(var e in d)e in b.style&&(b.style[e]="translate3d(1px,1px,1px)",c=pa.getComputedStyle(b).getPropertyValue(d[e]));document.body.removeChild(b);a=c&&"none"!==c}return a}}(); +function Re(a,b){var c=a.style;c.WebkitTransform=b;c.MozTransform=b;c.b=b;c.msTransform=b;c.transform=b;if((c=Ee)&&!(c=Ne["9.0"])){for(var c=0,d=qa(String(Ie)).split("."),e=qa("9.0").split("."),f=Math.max(d.length,e.length),g=0;0==c&&g<f;g++){var h=d[g]||"",l=e[g]||"",m=RegExp("(\\d*)(\\D*)","g"),n=RegExp("(\\d*)(\\D*)","g");do{var p=m.exec(h)||["","",""],q=n.exec(l)||["","",""];if(0==p[0].length&&0==q[0].length)break;c=ra(0==p[1].length?0:parseInt(p[1],10),0==q[1].length?0:parseInt(q[1],10))||ra(0== +p[2].length,0==q[2].length)||ra(p[2],q[2])}while(0==c)}c=Ne["9.0"]=0<=c}c&&(a.style.transformOrigin="0 0")}function Se(a,b){var c;if(Qe()){var d=Array(16);for(c=0;16>c;++c)d[c]=b[c].toFixed(6);Re(a,"matrix3d("+d.join(",")+")")}else if(Pe()){var d=[b[0],b[1],b[4],b[5],b[12],b[13]],e=Array(6);for(c=0;6>c;++c)e[c]=d[c].toFixed(6);Re(a,"matrix("+e.join(",")+")")}else a.style.left=Math.round(b[12])+"px",a.style.top=Math.round(b[13])+"px"}function Te(a,b){var c=b.parentNode;c&&c.replaceChild(a,b)} +function Ue(a){a&&a.parentNode&&a.parentNode.removeChild(a)}function Ve(a){for(;a.lastChild;)a.removeChild(a.lastChild)};function We(a,b,c){Wa.call(this,a);this.map=b;this.frameState=void 0!==c?c:null}y(We,Wa);function Xe(a){eb.call(this);this.element=a.element?a.element:null;this.a=this.S=null;this.s=[];this.render=a.render?a.render:na;a.target&&this.c(a.target)}y(Xe,eb);Xe.prototype.ka=function(){Ue(this.element);eb.prototype.ka.call(this)};Xe.prototype.i=function(){return this.a}; +Xe.prototype.setMap=function(a){this.a&&Ue(this.element);for(var b=0,c=this.s.length;b<c;++b)Ka(this.s[b]);this.s.length=0;if(this.a=a)(this.S?this.S:a.v).appendChild(this.element),this.render!==na&&this.s.push(B(a,"postrender",this.render,this)),a.render()};Xe.prototype.c=function(a){this.S="string"===typeof a?document.getElementById(a):a};function Ye(){this.g=0;this.f={};this.a=this.b=null}k=Ye.prototype;k.clear=function(){this.g=0;this.f={};this.a=this.b=null};function Ze(a,b){return a.f.hasOwnProperty(b)}k.forEach=function(a,b){for(var c=this.b;c;)a.call(b,c.pc,c.cc,this),c=c.yb};k.get=function(a){a=this.f[a];if(a===this.a)return a.pc;a===this.b?(this.b=this.b.yb,this.b.kc=null):(a.yb.kc=a.kc,a.kc.yb=a.yb);a.yb=null;a.kc=this.a;this.a=this.a.yb=a;return a.pc};k.wc=function(){return this.g}; +k.N=function(){var a=Array(this.g),b=0,c;for(c=this.a;c;c=c.kc)a[b++]=c.cc;return a};k.zc=function(){var a=Array(this.g),b=0,c;for(c=this.a;c;c=c.kc)a[b++]=c.pc;return a};k.pop=function(){var a=this.b;delete this.f[a.cc];a.yb&&(a.yb.kc=null);this.b=a.yb;this.b||(this.a=null);--this.g;return a.pc};k.replace=function(a,b){this.get(a);this.f[a].pc=b};k.set=function(a,b){var c={cc:a,yb:null,kc:this.a,pc:b};this.a?this.a.yb=c:this.b=c;this.a=c;this.f[a]=c;++this.g};function $e(a,b,c,d){return void 0!==d?(d[0]=a,d[1]=b,d[2]=c,d):[a,b,c]}function af(a){var b=a[0],c=Array(b),d=1<<b-1,e,f;for(e=0;e<b;++e)f=48,a[1]&d&&(f+=1),a[2]&d&&(f+=2),c[e]=String.fromCharCode(f),d>>=1;return c.join("")};function bf(a){Ye.call(this);this.c=void 0!==a?a:2048}y(bf,Ye);function cf(a){return a.wc()>a.c}bf.prototype.Lc=function(a){for(var b,c;cf(this)&&!(b=this.b.pc,c=b.ma[0].toString(),c in a&&a[c].contains(b.ma));)Ta(this.pop())};function df(a,b){$a.call(this);this.ma=a;this.state=b;this.a=null;this.key=""}y(df,$a);function ef(a){a.b("change")}df.prototype.ib=function(){return w(this).toString()};df.prototype.i=function(){return this.ma};df.prototype.V=function(){return this.state};function ff(a,b,c){void 0===c&&(c=[0,0]);c[0]=a[0]+2*b;c[1]=a[1]+2*b;return c}function gf(a,b,c){void 0===c&&(c=[0,0]);c[0]=a[0]*b+.5|0;c[1]=a[1]*b+.5|0;return c}function hf(a,b){if(Array.isArray(a))return a;void 0===b?b=[a,a]:(b[0]=a,b[1]=a);return b};function jf(a){eb.call(this);this.f=yc(a.projection);this.l=kf(a.attributions);this.R=a.logo;this.za=void 0!==a.state?a.state:"ready";this.D=void 0!==a.wrapX?a.wrapX:!1}y(jf,eb);function kf(a){if("string"===typeof a)return[new je({html:a})];if(a instanceof je)return[a];if(Array.isArray(a)){for(var b=a.length,c=Array(b),d=0;d<b;d++){var e=a[d];c[d]="string"===typeof e?new je({html:e}):e}return c}return null}k=jf.prototype;k.ra=na;k.wa=function(){return this.l};k.ua=function(){return this.R};k.xa=function(){return this.f}; +k.V=function(){return this.za};k.sa=function(){this.u()};k.oa=function(a){this.l=kf(a);this.u()};function lf(a,b){a.za=b;a.u()};function mf(a){this.minZoom=void 0!==a.minZoom?a.minZoom:0;this.b=a.resolutions;this.maxZoom=this.b.length-1;this.g=void 0!==a.origin?a.origin:null;this.c=null;void 0!==a.origins&&(this.c=a.origins);var b=a.extent;void 0===b||this.g||this.c||(this.g=fc(b));this.i=null;void 0!==a.tileSizes&&(this.i=a.tileSizes);this.o=void 0!==a.tileSize?a.tileSize:this.i?null:256;this.s=void 0!==b?b:null;this.a=null;this.f=[0,0];void 0!==a.sizes?this.a=a.sizes.map(function(a){return new fe(Math.min(0,a[0]),Math.max(a[0]- +1,-1),Math.min(0,a[1]),Math.max(a[1]-1,-1))},this):b&&nf(this,b)}var of=[0,0,0];k=mf.prototype;k.yg=function(a,b,c){a=pf(this,a,b);for(var d=a.ca,e=a.ea;d<=e;++d)for(var f=a.fa,g=a.ga;f<=g;++f)c([b,d,f])};function qf(a,b,c,d,e){e=a.Ea(b,e);for(b=b[0]-1;b>=a.minZoom;){if(c.call(null,b,pf(a,e,b,d)))return!0;--b}return!1}k.H=function(){return this.s};k.Ig=function(){return this.maxZoom};k.Jg=function(){return this.minZoom};k.Ia=function(a){return this.g?this.g:this.c[a]};k.$=function(a){return this.b[a]}; +k.Kh=function(){return this.b};function rf(a,b,c,d){return b[0]<a.maxZoom?(d=a.Ea(b,d),pf(a,d,b[0]+1,c)):null}function sf(a,b,c,d){tf(a,b[0],b[1],c,!1,of);var e=of[1],f=of[2];tf(a,b[2],b[3],c,!0,of);a=of[1];b=of[2];void 0!==d?(d.ca=e,d.ea=a,d.fa=f,d.ga=b):d=new fe(e,a,f,b);return d}function pf(a,b,c,d){c=a.$(c);return sf(a,b,c,d)}function uf(a,b){var c=a.Ia(b[0]),d=a.$(b[0]),e=hf(a.Ja(b[0]),a.f);return[c[0]+(b[1]+.5)*e[0]*d,c[1]+(b[2]+.5)*e[1]*d]} +k.Ea=function(a,b){var c=this.Ia(a[0]),d=this.$(a[0]),e=hf(this.Ja(a[0]),this.f),f=c[0]+a[1]*e[0]*d,c=c[1]+a[2]*e[1]*d;return Wb(f,c,f+e[0]*d,c+e[1]*d,b)};k.Zd=function(a,b,c){return tf(this,a[0],a[1],b,!1,c)};function tf(a,b,c,d,e,f){var g=a.Lb(d),h=d/a.$(g),l=a.Ia(g);a=hf(a.Ja(g),a.f);b=h*Math.floor((b-l[0])/d+(e?.5:0))/a[0];c=h*Math.floor((c-l[1])/d+(e?0:.5))/a[1];e?(b=Math.ceil(b)-1,c=Math.ceil(c)-1):(b=Math.floor(b),c=Math.floor(c));return $e(g,b,c,f)} +k.qd=function(a,b,c){b=this.$(b);return tf(this,a[0],a[1],b,!1,c)};k.Ja=function(a){return this.o?this.o:this.i[a]};k.Lb=function(a,b){var c=kb(this.b,a,b||0);return sa(c,this.minZoom,this.maxZoom)};function nf(a,b){for(var c=a.b.length,d=Array(c),e=a.minZoom;e<c;++e)d[e]=pf(a,b,e);a.a=d}function vf(a){var b=a.l;if(!b){var b=wf(a),c=xf(b,void 0,void 0),b=new mf({extent:b,origin:fc(b),resolutions:c,tileSize:void 0});a.l=b}return b} +function yf(a){var b={};Ea(b,void 0!==a?a:{});void 0===b.extent&&(b.extent=yc("EPSG:3857").H());b.resolutions=xf(b.extent,b.maxZoom,b.tileSize);delete b.maxZoom;return new mf(b)}function xf(a,b,c){b=void 0!==b?b:42;var d=jc(a);a=ic(a);c=hf(void 0!==c?c:256);c=Math.max(a/c[0],d/c[1]);b+=1;d=Array(b);for(a=0;a<b;++a)d[a]=c/Math.pow(2,a);return d}function wf(a){a=yc(a);var b=a.H();b||(a=180*uc.degrees/a.$b(),b=Wb(-a,-a,a,a));return b};function zf(a){jf.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,state:a.state,wrapX:a.wrapX});this.ia=void 0!==a.opaque?a.opaque:!1;this.ta=void 0!==a.tilePixelRatio?a.tilePixelRatio:1;this.tileGrid=void 0!==a.tileGrid?a.tileGrid:null;this.a=new bf(a.cacheSize);this.o=[0,0];this.cc=""}y(zf,jf);k=zf.prototype;k.Ah=function(){return cf(this.a)};k.Lc=function(a,b){var c=this.pd(a);c&&c.Lc(b)}; +function Af(a,b,c,d,e){b=a.pd(b);if(!b)return!1;for(var f=!0,g,h,l=d.ca;l<=d.ea;++l)for(var m=d.fa;m<=d.ga;++m)g=a.Eb(c,l,m),h=!1,Ze(b,g)&&(g=b.get(g),(h=2===g.V())&&(h=!1!==e(g))),h||(f=!1);return f}k.Ud=function(){return 0};function Bf(a,b){a.cc!==b&&(a.cc=b,a.u())}k.Eb=function(a,b,c){return a+"/"+b+"/"+c};k.jf=function(){return this.ia};k.Na=function(){return this.tileGrid};k.eb=function(a){return this.tileGrid?this.tileGrid:vf(a)};k.pd=function(a){var b=this.f;return b&&!Oc(b,a)?null:this.a}; +k.bc=function(){return this.ta};k.$d=function(a,b,c){c=this.eb(c);b=this.bc(b);a=hf(c.Ja(a),this.o);return 1==b?a:gf(a,b,this.o)};function Cf(a,b,c){var d=void 0!==c?c:a.f;c=a.eb(d);if(a.D&&d.g){var e=b;b=e[0];a=uf(c,e);d=wf(d);Sb(d,a)?b=e:(e=ic(d),a[0]+=e*Math.ceil((d[0]-a[0])/e),b=c.qd(a,b))}e=b[0];d=b[1];a=b[2];if(c.minZoom>e||e>c.maxZoom)c=!1;else{var f=c.H();c=(c=f?pf(c,f,e):c.a?c.a[e]:null)?ge(c,d,a):!0}return c?b:null}k.sa=function(){this.a.clear();this.u()};k.Yf=na; +function Df(a,b){Wa.call(this,a);this.tile=b}y(Df,Wa);function Ef(a){a=a?a:{};this.R=document.createElement("UL");this.v=document.createElement("LI");this.R.appendChild(this.v);this.v.style.display="none";this.f=void 0!==a.collapsed?a.collapsed:!0;this.o=void 0!==a.collapsible?a.collapsible:!0;this.o||(this.f=!1);var b=void 0!==a.className?a.className:"ol-attribution",c=void 0!==a.tipLabel?a.tipLabel:"Attributions",d=void 0!==a.collapseLabel?a.collapseLabel:"\u00bb";"string"===typeof d?(this.A=document.createElement("span"),this.A.textContent=d):this.A= +d;d=void 0!==a.label?a.label:"i";"string"===typeof d?(this.C=document.createElement("span"),this.C.textContent=d):this.C=d;var e=this.o&&!this.f?this.A:this.C,d=document.createElement("button");d.setAttribute("type","button");d.title=c;d.appendChild(e);B(d,"click",this.am,this);c=document.createElement("div");c.className=b+" ol-unselectable ol-control"+(this.f&&this.o?" ol-collapsed":"")+(this.o?"":" ol-uncollapsible");c.appendChild(this.R);c.appendChild(d);Xe.call(this,{element:c,render:a.render? +a.render:Ff,target:a.target});this.D=!0;this.j={};this.l={};this.T={}}y(Ef,Xe); +function Ff(a){if(a=a.frameState){var b,c,d,e,f,g,h,l,m,n,p,q=a.layerStatesArray,r=Ea({},a.attributions),u={},x=a.viewState.projection;c=0;for(b=q.length;c<b;c++)if(g=q[c].layer.ha())if(n=w(g).toString(),m=g.l)for(d=0,e=m.length;d<e;d++)if(h=m[d],l=w(h).toString(),!(l in r)){if(f=a.usedTiles[n]){var v=g.eb(x);a:{p=h;var D=x;if(p.b){var A,z,F,N=void 0;for(N in f)if(N in p.b){F=f[N];var K;A=0;for(z=p.b[N].length;A<z;++A){K=p.b[N][A];if(ie(K,F)){p=!0;break a}var X=pf(v,wf(D),parseInt(N,10)),oa=X.ea- +X.ca+1;if(F.ca<X.ca||F.ea>X.ea)if(ie(K,new fe(xa(F.ca,oa),xa(F.ea,oa),F.fa,F.ga))||F.ea-F.ca+1>oa&&ie(K,X)){p=!0;break a}}}p=!1}else p=!0}}else p=!1;p?(l in u&&delete u[l],r[l]=h):u[l]=h}b=[r,u];c=b[0];b=b[1];for(var H in this.j)H in c?(this.l[H]||(this.j[H].style.display="",this.l[H]=!0),delete c[H]):H in b?(this.l[H]&&(this.j[H].style.display="none",delete this.l[H]),delete b[H]):(Ue(this.j[H]),delete this.j[H],delete this.l[H]);for(H in c)d=document.createElement("LI"),d.innerHTML=c[H].a,this.R.appendChild(d), +this.j[H]=d,this.l[H]=!0;for(H in b)d=document.createElement("LI"),d.innerHTML=b[H].a,d.style.display="none",this.R.appendChild(d),this.j[H]=d;H=!Ha(this.l)||!Ha(a.logos);this.D!=H&&(this.element.style.display=H?"":"none",this.D=H);H&&Ha(this.l)?this.element.classList.add("ol-logo-only"):this.element.classList.remove("ol-logo-only");var ya;a=a.logos;H=this.T;for(ya in H)ya in a||(Ue(H[ya]),delete H[ya]);for(var Ua in a)b=a[Ua],b instanceof HTMLElement&&(this.v.appendChild(b),H[Ua]=b),Ua in H||(ya= +new Image,ya.src=Ua,""===b?c=ya:(c=document.createElement("a"),c.href=b,c.appendChild(ya)),this.v.appendChild(c),H[Ua]=c);this.v.style.display=Ha(a)?"none":""}else this.D&&(this.element.style.display="none",this.D=!1)}k=Ef.prototype;k.am=function(a){a.preventDefault();Gf(this)};function Gf(a){a.element.classList.toggle("ol-collapsed");a.f?Te(a.A,a.C):Te(a.C,a.A);a.f=!a.f}k.$l=function(){return this.o}; +k.cm=function(a){this.o!==a&&(this.o=a,this.element.classList.toggle("ol-uncollapsible"),!a&&this.f&&Gf(this))};k.bm=function(a){this.o&&this.f!==a&&Gf(this)};k.Zl=function(){return this.f};function Hf(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-rotate",c=void 0!==a.label?a.label:"\u21e7";this.f=null;"string"===typeof c?(this.f=document.createElement("span"),this.f.className="ol-compass",this.f.textContent=c):(this.f=c,this.f.classList.add("ol-compass"));var d=a.tipLabel?a.tipLabel:"Reset rotation",c=document.createElement("button");c.className=b+"-reset";c.setAttribute("type","button");c.title=d;c.appendChild(this.f);B(c,"click",Hf.prototype.A,this);d=document.createElement("div"); +d.className=b+" ol-unselectable ol-control";d.appendChild(c);b=a.render?a.render:If;this.o=a.resetNorth?a.resetNorth:void 0;Xe.call(this,{element:d,render:b,target:a.target});this.j=void 0!==a.duration?a.duration:250;this.l=void 0!==a.autoHide?a.autoHide:!0;this.v=void 0;this.l&&this.element.classList.add("ol-hidden")}y(Hf,Xe); +Hf.prototype.A=function(a){a.preventDefault();if(void 0!==this.o)this.o();else{a=this.a;var b=a.aa();if(b){var c=b.La();void 0!==c&&(0<this.j&&(c%=2*Math.PI,c<-Math.PI&&(c+=2*Math.PI),c>Math.PI&&(c-=2*Math.PI),a.Wa(de({rotation:c,duration:this.j,easing:Zd}))),b.ie(0))}}}; +function If(a){if(a=a.frameState){a=a.viewState.rotation;if(a!=this.v){var b="rotate("+a+"rad)";if(this.l){var c=this.element.classList.contains("ol-hidden");c||0!==a?c&&0!==a&&this.element.classList.remove("ol-hidden"):this.element.classList.add("ol-hidden")}this.f.style.msTransform=b;this.f.style.webkitTransform=b;this.f.style.transform=b}this.v=a}};function Jf(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-zoom",c=void 0!==a.delta?a.delta:1,d=void 0!==a.zoomInLabel?a.zoomInLabel:"+",e=void 0!==a.zoomOutLabel?a.zoomOutLabel:"\u2212",f=void 0!==a.zoomInTipLabel?a.zoomInTipLabel:"Zoom in",g=void 0!==a.zoomOutTipLabel?a.zoomOutTipLabel:"Zoom out",h=document.createElement("button");h.className=b+"-in";h.setAttribute("type","button");h.title=f;h.appendChild("string"===typeof d?document.createTextNode(d):d);B(h,"click",Jf.prototype.l.bind(this, +c));d=document.createElement("button");d.className=b+"-out";d.setAttribute("type","button");d.title=g;d.appendChild("string"===typeof e?document.createTextNode(e):e);B(d,"click",Jf.prototype.l.bind(this,-c));c=document.createElement("div");c.className=b+" ol-unselectable ol-control";c.appendChild(h);c.appendChild(d);Xe.call(this,{element:c,target:a.target});this.f=void 0!==a.duration?a.duration:250}y(Jf,Xe); +Jf.prototype.l=function(a,b){b.preventDefault();var c=this.a,d=c.aa();if(d){var e=d.$();e&&(0<this.f&&c.Wa(ee({resolution:e,duration:this.f,easing:Zd})),c=d.constrainResolution(e,a),d.Ub(c))}};function Kf(a){a=a?a:{};var b=new le;(void 0!==a.zoom?a.zoom:1)&&b.push(new Jf(a.zoomOptions));(void 0!==a.rotate?a.rotate:1)&&b.push(new Hf(a.rotateOptions));(void 0!==a.attribution?a.attribution:1)&&b.push(new Ef(a.attributionOptions));return b};function Lf(a){a=a?a:{};this.f=void 0!==a.className?a.className:"ol-full-screen";var b=void 0!==a.label?a.label:"\u2922";this.o="string"===typeof b?document.createTextNode(b):b;b=void 0!==a.labelActive?a.labelActive:"\u00d7";this.j="string"===typeof b?document.createTextNode(b):b;var c=a.tipLabel?a.tipLabel:"Toggle full-screen",b=document.createElement("button");b.className=this.f+"-"+Mf();b.setAttribute("type","button");b.title=c;b.appendChild(this.o);B(b,"click",this.C,this);c=document.createElement("div"); +c.className=this.f+" ol-unselectable ol-control "+(Nf()?"":"ol-unsupported");c.appendChild(b);Xe.call(this,{element:c,target:a.target});this.A=void 0!==a.keys?a.keys:!1;this.l=a.source}y(Lf,Xe); +Lf.prototype.C=function(a){a.preventDefault();Nf()&&(a=this.a)&&(Mf()?document.exitFullscreen?document.exitFullscreen():document.msExitFullscreen?document.msExitFullscreen():document.mozCancelFullScreen?document.mozCancelFullScreen():document.webkitExitFullscreen&&document.webkitExitFullscreen():(a=this.l?"string"===typeof this.l?document.getElementById(this.l):this.l:a.yc(),this.A?a.mozRequestFullScreenWithKeys?a.mozRequestFullScreenWithKeys():a.webkitRequestFullscreen?a.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT): +Of(a):Of(a)))};Lf.prototype.v=function(){var a=this.element.firstElementChild,b=this.a;Mf()?(a.className=this.f+"-true",Te(this.j,this.o)):(a.className=this.f+"-false",Te(this.o,this.j));b&&b.Xc()};Lf.prototype.setMap=function(a){Xe.prototype.setMap.call(this,a);a&&this.s.push(B(pa.document,Pf(),this.v,this))}; +function Nf(){var a=document.body;return!!(a.webkitRequestFullscreen||a.mozRequestFullScreen&&document.mozFullScreenEnabled||a.msRequestFullscreen&&document.msFullscreenEnabled||a.requestFullscreen&&document.fullscreenEnabled)}function Mf(){return!!(document.webkitIsFullScreen||document.mozFullScreen||document.msFullscreenElement||document.fullscreenElement)} +function Of(a){a.requestFullscreen?a.requestFullscreen():a.msRequestFullscreen?a.msRequestFullscreen():a.mozRequestFullScreen?a.mozRequestFullScreen():a.webkitRequestFullscreen&&a.webkitRequestFullscreen()}var Pf=function(){var a;return function(){if(!a){var b=document.body;b.webkitRequestFullscreen?a="webkitfullscreenchange":b.mozRequestFullScreen?a="mozfullscreenchange":b.msRequestFullscreen?a="MSFullscreenChange":b.requestFullscreen&&(a="fullscreenchange")}return a}}();function Qf(a){a=a?a:{};var b=document.createElement("DIV");b.className=void 0!==a.className?a.className:"ol-mouse-position";Xe.call(this,{element:b,render:a.render?a.render:Rf,target:a.target});B(this,gb("projection"),this.dm,this);a.coordinateFormat&&this.ei(a.coordinateFormat);a.projection&&this.ih(yc(a.projection));this.v=void 0!==a.undefinedHTML?a.undefinedHTML:"";this.j=b.innerHTML;this.o=this.l=this.f=null}y(Qf,Xe); +function Rf(a){a=a.frameState;a?this.f!=a.viewState.projection&&(this.f=a.viewState.projection,this.l=null):this.f=null;Sf(this,this.o)}k=Qf.prototype;k.dm=function(){this.l=null};k.Cg=function(){return this.get("coordinateFormat")};k.hh=function(){return this.get("projection")};k.Xk=function(a){this.o=this.a.Td(a);Sf(this,this.o)};k.Yk=function(){Sf(this,null);this.o=null}; +k.setMap=function(a){Xe.prototype.setMap.call(this,a);a&&(a=a.a,this.s.push(B(a,"mousemove",this.Xk,this),B(a,"mouseout",this.Yk,this)))};k.ei=function(a){this.set("coordinateFormat",a)};k.ih=function(a){this.set("projection",a)};function Sf(a,b){var c=a.v;if(b&&a.f){if(!a.l){var d=a.hh();a.l=d?Bc(a.f,d):Qc}if(d=a.a.Ma(b))a.l(d,d),c=(c=a.Cg())?c(d):d.toString()}a.j&&c==a.j||(a.element.innerHTML=c,a.j=c)};function Tf(a,b){var c=a;b&&(c=ka(a,b));"function"!=ca(aa.setImmediate)||aa.Window&&aa.Window.prototype&&!Be("Edge")&&aa.Window.prototype.setImmediate==aa.setImmediate?(Uf||(Uf=Vf()),Uf(c)):aa.setImmediate(c)}var Uf; +function Vf(){var a=aa.MessageChannel;"undefined"===typeof a&&"undefined"!==typeof window&&window.postMessage&&window.addEventListener&&!Be("Presto")&&(a=function(){var a=document.createElement("IFRAME");a.style.display="none";a.src="";document.documentElement.appendChild(a);var b=a.contentWindow,a=b.document;a.open();a.write("");a.close();var c="callImmediate"+Math.random(),d="file:"==b.location.protocol?"*":b.location.protocol+"//"+b.location.host,a=ka(function(a){if(("*"==d||a.origin==d)&&a.data== +c)this.port1.onmessage()},this);b.addEventListener("message",a,!1);this.port1={};this.port2={postMessage:function(){b.postMessage(c,d)}}});if("undefined"!==typeof a&&!Be("Trident")&&!Be("MSIE")){var b=new a,c={},d=c;b.port1.onmessage=function(){if(void 0!==c.next){c=c.next;var a=c.rg;c.rg=null;a()}};return function(a){d.next={rg:a};d=d.next;b.port2.postMessage(0)}}return"undefined"!==typeof document&&"onreadystatechange"in document.createElement("SCRIPT")?function(a){var b=document.createElement("SCRIPT"); +b.onreadystatechange=function(){b.onreadystatechange=null;b.parentNode.removeChild(b);b=null;a();a=null};document.documentElement.appendChild(b)}:function(a){aa.setTimeout(a,0)}};function Wf(a,b,c){Wa.call(this,a);this.b=b;a=c?c:{};this.buttons=Xf(a);this.pressure=Yf(a,this.buttons);this.bubbles="bubbles"in a?a.bubbles:!1;this.cancelable="cancelable"in a?a.cancelable:!1;this.view="view"in a?a.view:null;this.detail="detail"in a?a.detail:null;this.screenX="screenX"in a?a.screenX:0;this.screenY="screenY"in a?a.screenY:0;this.clientX="clientX"in a?a.clientX:0;this.clientY="clientY"in a?a.clientY:0;this.button="button"in a?a.button:0;this.relatedTarget="relatedTarget"in a?a.relatedTarget: +null;this.pointerId="pointerId"in a?a.pointerId:0;this.width="width"in a?a.width:0;this.height="height"in a?a.height:0;this.pointerType="pointerType"in a?a.pointerType:"";this.isPrimary="isPrimary"in a?a.isPrimary:!1;b.preventDefault&&(this.preventDefault=function(){b.preventDefault()})}y(Wf,Wa);function Xf(a){if(a.buttons||Zf)a=a.buttons;else switch(a.which){case 1:a=1;break;case 2:a=4;break;case 3:a=2;break;default:a=0}return a} +function Yf(a,b){var c=0;a.pressure?c=a.pressure:c=b?.5:0;return c}var Zf=!1;try{Zf=1===(new MouseEvent("click",{buttons:1})).buttons}catch(a){};var $f=["experimental-webgl","webgl","webkit-3d","moz-webgl"];function ag(a,b){var c,d,e=$f.length;for(d=0;d<e;++d)try{if(c=a.getContext($f[d],b))return c}catch(f){}return null};var bg,cg="undefined"!==typeof navigator?navigator.userAgent.toLowerCase():"",dg=-1!==cg.indexOf("firefox"),eg=-1!==cg.indexOf("safari")&&-1===cg.indexOf("chrom"),fg=-1!==cg.indexOf("macintosh"),gg=pa.devicePixelRatio||1,hg=!1,ig=function(){if(!("HTMLCanvasElement"in pa))return!1;try{var a=Oe();return a?(a.setLineDash&&(hg=!0),!0):!1}catch(b){return!1}}(),jg="DeviceOrientationEvent"in pa,kg="geolocation"in pa.navigator,lg="ontouchstart"in pa,mg="PointerEvent"in pa,ng=!!pa.navigator.msPointerEnabled, +og=!1,pg,qg=[];if("WebGLRenderingContext"in pa)try{var rg=ag(document.createElement("CANVAS"),{failIfMajorPerformanceCaveat:!0});rg&&(og=!0,pg=rg.getParameter(rg.MAX_TEXTURE_SIZE),qg=rg.getSupportedExtensions())}catch(a){}bg=og;ma=qg;la=pg;function sg(a,b){this.b=a;this.c=b};function tg(a){sg.call(this,a,{mousedown:this.rl,mousemove:this.sl,mouseup:this.vl,mouseover:this.ul,mouseout:this.tl});this.a=a.g;this.g=[]}y(tg,sg);function ug(a,b){for(var c=a.g,d=b.clientX,e=b.clientY,f=0,g=c.length,h;f<g&&(h=c[f]);f++){var l=Math.abs(e-h[1]);if(25>=Math.abs(d-h[0])&&25>=l)return!0}return!1}function vg(a){var b=wg(a,a),c=b.preventDefault;b.preventDefault=function(){a.preventDefault();c()};b.pointerId=1;b.isPrimary=!0;b.pointerType="mouse";return b}k=tg.prototype; +k.rl=function(a){if(!ug(this,a)){if((1).toString()in this.a){var b=vg(a);xg(this.b,yg,b,a);delete this.a[(1).toString()]}b=vg(a);this.a[(1).toString()]=a;xg(this.b,zg,b,a)}};k.sl=function(a){if(!ug(this,a)){var b=vg(a);xg(this.b,Ag,b,a)}};k.vl=function(a){if(!ug(this,a)){var b=this.a[(1).toString()];b&&b.button===a.button&&(b=vg(a),xg(this.b,Bg,b,a),delete this.a[(1).toString()])}};k.ul=function(a){if(!ug(this,a)){var b=vg(a);Cg(this.b,b,a)}}; +k.tl=function(a){if(!ug(this,a)){var b=vg(a);Dg(this.b,b,a)}};function Eg(a){sg.call(this,a,{MSPointerDown:this.Al,MSPointerMove:this.Bl,MSPointerUp:this.El,MSPointerOut:this.Cl,MSPointerOver:this.Dl,MSPointerCancel:this.zl,MSGotPointerCapture:this.xl,MSLostPointerCapture:this.yl});this.a=a.g;this.g=["","unavailable","touch","pen","mouse"]}y(Eg,sg);function Fg(a,b){var c=b;ea(b.pointerType)&&(c=wg(b,b),c.pointerType=a.g[b.pointerType]);return c}k=Eg.prototype;k.Al=function(a){this.a[a.pointerId.toString()]=a;var b=Fg(this,a);xg(this.b,zg,b,a)}; +k.Bl=function(a){var b=Fg(this,a);xg(this.b,Ag,b,a)};k.El=function(a){var b=Fg(this,a);xg(this.b,Bg,b,a);delete this.a[a.pointerId.toString()]};k.Cl=function(a){var b=Fg(this,a);Dg(this.b,b,a)};k.Dl=function(a){var b=Fg(this,a);Cg(this.b,b,a)};k.zl=function(a){var b=Fg(this,a);xg(this.b,yg,b,a);delete this.a[a.pointerId.toString()]};k.yl=function(a){this.b.b(new Wf("lostpointercapture",a,a))};k.xl=function(a){this.b.b(new Wf("gotpointercapture",a,a))};function Gg(a){sg.call(this,a,{pointerdown:this.lo,pointermove:this.mo,pointerup:this.po,pointerout:this.no,pointerover:this.oo,pointercancel:this.ko,gotpointercapture:this.Gk,lostpointercapture:this.ql})}y(Gg,sg);k=Gg.prototype;k.lo=function(a){Hg(this.b,a)};k.mo=function(a){Hg(this.b,a)};k.po=function(a){Hg(this.b,a)};k.no=function(a){Hg(this.b,a)};k.oo=function(a){Hg(this.b,a)};k.ko=function(a){Hg(this.b,a)};k.ql=function(a){Hg(this.b,a)};k.Gk=function(a){Hg(this.b,a)};function Ig(a,b){sg.call(this,a,{touchstart:this.sp,touchmove:this.rp,touchend:this.qp,touchcancel:this.pp});this.a=a.g;this.l=b;this.g=void 0;this.i=0;this.f=void 0}y(Ig,sg);k=Ig.prototype;k.ci=function(){this.i=0;this.f=void 0}; +function Jg(a,b,c){b=wg(b,c);b.pointerId=c.identifier+2;b.bubbles=!0;b.cancelable=!0;b.detail=a.i;b.button=0;b.buttons=1;b.width=c.webkitRadiusX||c.radiusX||0;b.height=c.webkitRadiusY||c.radiusY||0;b.pressure=c.webkitForce||c.force||.5;b.isPrimary=a.g===c.identifier;b.pointerType="touch";b.clientX=c.clientX;b.clientY=c.clientY;b.screenX=c.screenX;b.screenY=c.screenY;return b} +function Kg(a,b,c){function d(){b.preventDefault()}var e=Array.prototype.slice.call(b.changedTouches),f=e.length,g,h;for(g=0;g<f;++g)h=Jg(a,b,e[g]),h.preventDefault=d,c.call(a,b,h)} +k.sp=function(a){var b=a.touches,c=Object.keys(this.a),d=c.length;if(d>=b.length){var e=[],f,g,h;for(f=0;f<d;++f){g=c[f];h=this.a[g];var l;if(!(l=1==g))a:{l=b.length;for(var m,n=0;n<l;n++)if(m=b[n],m.identifier===g-2){l=!0;break a}l=!1}l||e.push(h.out)}for(f=0;f<e.length;++f)this.Ue(a,e[f])}b=a.changedTouches[0];c=Object.keys(this.a).length;if(0===c||1===c&&(1).toString()in this.a)this.g=b.identifier,void 0!==this.f&&pa.clearTimeout(this.f);Lg(this,a);this.i++;Kg(this,a,this.fo)}; +k.fo=function(a,b){this.a[b.pointerId]={target:b.target,out:b,Lh:b.target};var c=this.b;b.bubbles=!0;xg(c,Mg,b,a);c=this.b;b.bubbles=!1;xg(c,Ng,b,a);xg(this.b,zg,b,a)};k.rp=function(a){a.preventDefault();Kg(this,a,this.wl)};k.wl=function(a,b){var c=this.a[b.pointerId];if(c){var d=c.out,e=c.Lh;xg(this.b,Ag,b,a);d&&e!==b.target&&(d.relatedTarget=b.target,b.relatedTarget=e,d.target=e,b.target?(Dg(this.b,d,a),Cg(this.b,b,a)):(b.target=e,b.relatedTarget=null,this.Ue(a,b)));c.out=b;c.Lh=b.target}}; +k.qp=function(a){Lg(this,a);Kg(this,a,this.tp)};k.tp=function(a,b){xg(this.b,Bg,b,a);this.b.out(b,a);var c=this.b;b.bubbles=!1;xg(c,Og,b,a);delete this.a[b.pointerId];b.isPrimary&&(this.g=void 0,this.f=pa.setTimeout(this.ci.bind(this),200))};k.pp=function(a){Kg(this,a,this.Ue)};k.Ue=function(a,b){xg(this.b,yg,b,a);this.b.out(b,a);var c=this.b;b.bubbles=!1;xg(c,Og,b,a);delete this.a[b.pointerId];b.isPrimary&&(this.g=void 0,this.f=pa.setTimeout(this.ci.bind(this),200))}; +function Lg(a,b){var c=a.l.g,d=b.changedTouches[0];if(a.g===d.identifier){var e=[d.clientX,d.clientY];c.push(e);pa.setTimeout(function(){nb(c,e)},2500)}};function Pg(a){$a.call(this);this.i=a;this.g={};this.c={};this.a=[];mg?Qg(this,new Gg(this)):ng?Qg(this,new Eg(this)):(a=new tg(this),Qg(this,a),lg&&Qg(this,new Ig(this,a)));a=this.a.length;for(var b,c=0;c<a;c++)b=this.a[c],Rg(this,Object.keys(b.c))}y(Pg,$a);function Qg(a,b){var c=Object.keys(b.c);c&&(c.forEach(function(a){var c=b.c[a];c&&(this.c[a]=c.bind(b))},a),a.a.push(b))}Pg.prototype.f=function(a){var b=this.c[a.type];b&&b(a)}; +function Rg(a,b){b.forEach(function(a){B(this.i,a,this.f,this)},a)}function Sg(a,b){b.forEach(function(a){Qa(this.i,a,this.f,this)},a)}function wg(a,b){for(var c={},d,e=0,f=Tg.length;e<f;e++)d=Tg[e][0],c[d]=a[d]||b[d]||Tg[e][1];return c}Pg.prototype.out=function(a,b){a.bubbles=!0;xg(this,Ug,a,b)};function Dg(a,b,c){a.out(b,c);var d=b.target,e=b.relatedTarget;d&&e&&d.contains(e)||(b.bubbles=!1,xg(a,Og,b,c))} +function Cg(a,b,c){b.bubbles=!0;xg(a,Mg,b,c);var d=b.target,e=b.relatedTarget;d&&e&&d.contains(e)||(b.bubbles=!1,xg(a,Ng,b,c))}function xg(a,b,c,d){a.b(new Wf(b,d,c))}function Hg(a,b){a.b(new Wf(b.type,b,b))}Pg.prototype.ka=function(){for(var a=this.a.length,b,c=0;c<a;c++)b=this.a[c],Sg(this,Object.keys(b.c));$a.prototype.ka.call(this)}; +var Ag="pointermove",zg="pointerdown",Bg="pointerup",Mg="pointerover",Ug="pointerout",Ng="pointerenter",Og="pointerleave",yg="pointercancel",Tg=[["bubbles",!1],["cancelable",!1],["view",null],["detail",null],["screenX",0],["screenY",0],["clientX",0],["clientY",0],["ctrlKey",!1],["altKey",!1],["shiftKey",!1],["metaKey",!1],["button",0],["relatedTarget",null],["buttons",0],["pointerId",0],["width",0],["height",0],["pressure",0],["tiltX",0],["tiltY",0],["pointerType",""],["hwTimestamp",0],["isPrimary", +!1],["type",""],["target",null],["currentTarget",null],["which",0]];function Vg(a,b,c,d,e){We.call(this,a,b,e);this.originalEvent=c;this.pixel=b.Td(c);this.coordinate=b.Ma(this.pixel);this.dragging=void 0!==d?d:!1}y(Vg,We);Vg.prototype.preventDefault=function(){We.prototype.preventDefault.call(this);this.originalEvent.preventDefault()};Vg.prototype.stopPropagation=function(){We.prototype.stopPropagation.call(this);this.originalEvent.stopPropagation()};function Wg(a,b,c,d,e){Vg.call(this,a,b,c.b,d,e);this.b=c}y(Wg,Vg); +function Xg(a){$a.call(this);this.f=a;this.l=0;this.o=!1;this.c=[];this.g=null;a=this.f.a;this.U=0;this.v={};this.i=new Pg(a);this.a=null;this.j=B(this.i,zg,this.$k,this);this.s=B(this.i,Ag,this.No,this)}y(Xg,$a);function Yg(a,b){var c;c=new Wg(Zg,a.f,b);a.b(c);0!==a.l?(pa.clearTimeout(a.l),a.l=0,c=new Wg($g,a.f,b),a.b(c)):a.l=pa.setTimeout(function(){this.l=0;var a=new Wg(ah,this.f,b);this.b(a)}.bind(a),250)} +function bh(a,b){b.type==ch||b.type==dh?delete a.v[b.pointerId]:b.type==eh&&(a.v[b.pointerId]=!0);a.U=Object.keys(a.v).length}k=Xg.prototype;k.Qg=function(a){bh(this,a);var b=new Wg(ch,this.f,a);this.b(b);!this.o&&0===a.button&&Yg(this,this.g);0===this.U&&(this.c.forEach(Ka),this.c.length=0,this.o=!1,this.g=null,Ta(this.a),this.a=null)}; +k.$k=function(a){bh(this,a);var b=new Wg(eh,this.f,a);this.b(b);this.g=a;0===this.c.length&&(this.a=new Pg(document),this.c.push(B(this.a,fh,this.Sl,this),B(this.a,ch,this.Qg,this),B(this.i,dh,this.Qg,this)))};k.Sl=function(a){if(a.clientX!=this.g.clientX||a.clientY!=this.g.clientY){this.o=!0;var b=new Wg(gh,this.f,a,this.o);this.b(b)}a.preventDefault()};k.No=function(a){this.b(new Wg(a.type,this.f,a,!(!this.g||a.clientX==this.g.clientX&&a.clientY==this.g.clientY)))}; +k.ka=function(){this.s&&(Ka(this.s),this.s=null);this.j&&(Ka(this.j),this.j=null);this.c.forEach(Ka);this.c.length=0;this.a&&(Ta(this.a),this.a=null);this.i&&(Ta(this.i),this.i=null);$a.prototype.ka.call(this)};var ah="singleclick",Zg="click",$g="dblclick",gh="pointerdrag",fh="pointermove",eh="pointerdown",ch="pointerup",dh="pointercancel",hh={Mp:ah,Bp:Zg,Cp:$g,Fp:gh,Ip:fh,Ep:eh,Lp:ch,Kp:"pointerover",Jp:"pointerout",Gp:"pointerenter",Hp:"pointerleave",Dp:dh};function ih(a){eb.call(this);var b=Ea({},a);b.opacity=void 0!==a.opacity?a.opacity:1;b.visible=void 0!==a.visible?a.visible:!0;b.zIndex=void 0!==a.zIndex?a.zIndex:0;b.maxResolution=void 0!==a.maxResolution?a.maxResolution:Infinity;b.minResolution=void 0!==a.minResolution?a.minResolution:0;this.G(b)}y(ih,eb); +function jh(a){var b=a.Pb(),c=a.kf(),d=a.xb(),e=a.H(),f=a.Qb(),g=a.Nb(),h=a.Ob();return{layer:a,opacity:sa(b,0,1),R:c,visible:d,Qc:!0,extent:e,zIndex:f,maxResolution:g,minResolution:Math.max(h,0)}}k=ih.prototype;k.H=function(){return this.get("extent")};k.Nb=function(){return this.get("maxResolution")};k.Ob=function(){return this.get("minResolution")};k.Pb=function(){return this.get("opacity")};k.xb=function(){return this.get("visible")};k.Qb=function(){return this.get("zIndex")}; +k.fc=function(a){this.set("extent",a)};k.nc=function(a){this.set("maxResolution",a)};k.oc=function(a){this.set("minResolution",a)};k.gc=function(a){this.set("opacity",a)};k.hc=function(a){this.set("visible",a)};k.ic=function(a){this.set("zIndex",a)};function kh(){};function lh(a,b,c,d,e,f){Wa.call(this,a,b);this.vectorContext=c;this.frameState=d;this.context=e;this.glContext=f}y(lh,Wa);function mh(a){var b=Ea({},a);delete b.source;ih.call(this,b);this.v=this.j=this.o=null;a.map&&this.setMap(a.map);B(this,gb("source"),this.fl,this);this.Fc(a.source?a.source:null)}y(mh,ih);function nh(a,b){return a.visible&&b>=a.minResolution&&b<a.maxResolution}k=mh.prototype;k.hf=function(a){a=a?a:[];a.push(jh(this));return a};k.ha=function(){return this.get("source")||null};k.kf=function(){var a=this.ha();return a?a.V():"undefined"};k.Lm=function(){this.u()}; +k.fl=function(){this.v&&(Ka(this.v),this.v=null);var a=this.ha();a&&(this.v=B(a,"change",this.Lm,this));this.u()};k.setMap=function(a){this.o&&(Ka(this.o),this.o=null);a||this.u();this.j&&(Ka(this.j),this.j=null);a&&(this.o=B(a,"precompose",function(a){var c=jh(this);c.Qc=!1;c.zIndex=Infinity;a.frameState.layerStatesArray.push(c);a.frameState.layerStates[w(this)]=c},this),this.j=B(this,"change",a.render,a),this.u())};k.Fc=function(a){this.set("source",a)};function oh(a,b,c,d,e){$a.call(this);this.l=e;this.extent=a;this.f=c;this.resolution=b;this.state=d}y(oh,$a);function ph(a){a.b("change")}oh.prototype.H=function(){return this.extent};oh.prototype.$=function(){return this.resolution};oh.prototype.V=function(){return this.state};function qh(a,b,c,d,e,f,g,h){ad(a);0===b&&0===c||dd(a,b,c);1==d&&1==e||ed(a,d,e);0!==f&&fd(a,f);0===g&&0===h||dd(a,g,h);return a}function rh(a,b){return a[0]==b[0]&&a[1]==b[1]&&a[4]==b[4]&&a[5]==b[5]&&a[12]==b[12]&&a[13]==b[13]}function sh(a,b,c){var d=a[1],e=a[5],f=a[13],g=b[0];b=b[1];c[0]=a[0]*g+a[4]*b+a[12];c[1]=d*g+e*b+f;return c};function th(a){bb.call(this);this.a=a}y(th,bb);k=th.prototype;k.ra=na;k.Cc=function(a,b,c,d){a=a.slice();sh(b.pixelToCoordinateMatrix,a,a);if(this.ra(a,b,qc,this))return c.call(d,this.a)};k.le=rc;k.Qd=function(a,b,c){return function(d,e){return Af(a,b,d,e,function(a){c[d]||(c[d]={});c[d][a.ma.toString()]=a})}};k.Om=function(a){2===a.target.V()&&uh(this)};function vh(a,b){var c=b.V();2!=c&&3!=c&&B(b,"change",a.Om,a);0==c&&(b.load(),c=b.V());return 2==c} +function uh(a){var b=a.a;b.xb()&&"ready"==b.kf()&&a.u()}function wh(a,b){b.Ah()&&a.postRenderFunctions.push(function(a,b,e){b=w(a).toString();a.Lc(e.viewState.projection,e.usedTiles[b])}.bind(null,b))}function xh(a,b){if(b){var c,d,e;d=0;for(e=b.length;d<e;++d)c=b[d],a[w(c).toString()]=c}}function yh(a,b){var c=b.R;void 0!==c&&("string"===typeof c?a.logos[c]="":fa(c)&&(a.logos[c.src]=c.href))} +function zh(a,b,c,d){b=w(b).toString();c=c.toString();b in a?c in a[b]?(a=a[b][c],d.ca<a.ca&&(a.ca=d.ca),d.ea>a.ea&&(a.ea=d.ea),d.fa<a.fa&&(a.fa=d.fa),d.ga>a.ga&&(a.ga=d.ga)):a[b][c]=d:(a[b]={},a[b][c]=d)}function Ah(a,b,c){return[b*(Math.round(a[0]/b)+c[0]%2/2),b*(Math.round(a[1]/b)+c[1]%2/2)]} +function Bh(a,b,c,d,e,f,g,h,l,m){var n=w(b).toString();n in a.wantedTiles||(a.wantedTiles[n]={});var p=a.wantedTiles[n];a=a.tileQueue;var q=c.minZoom,r,u,x,v,D,A;for(A=g;A>=q;--A)for(u=pf(c,f,A,u),x=c.$(A),v=u.ca;v<=u.ea;++v)for(D=u.fa;D<=u.ga;++D)g-A<=h?(r=b.ac(A,v,D,d,e),0==r.V()&&(p[r.ma.toString()]=!0,r.ib()in a.g||a.f([r,n,uf(c,r.ma),x])),void 0!==l&&l.call(m,r)):b.Yf(A,v,D,e)};function Ch(a){this.v=a.opacity;this.U=a.rotateWithView;this.j=a.rotation;this.i=a.scale;this.C=a.snapToPixel}k=Ch.prototype;k.qe=function(){return this.v};k.Xd=function(){return this.U};k.re=function(){return this.j};k.se=function(){return this.i};k.Yd=function(){return this.C};k.te=function(a){this.v=a};k.ue=function(a){this.j=a};k.ve=function(a){this.i=a};function Dh(a){a=a||{};this.c=void 0!==a.anchor?a.anchor:[.5,.5];this.f=null;this.a=void 0!==a.anchorOrigin?a.anchorOrigin:"top-left";this.o=void 0!==a.anchorXUnits?a.anchorXUnits:"fraction";this.s=void 0!==a.anchorYUnits?a.anchorYUnits:"fraction";var b=void 0!==a.crossOrigin?a.crossOrigin:null,c=void 0!==a.img?a.img:null,d=void 0!==a.imgSize?a.imgSize:null,e=a.src;void 0!==e&&0!==e.length||!c||(e=c.src||w(c).toString());var f=void 0!==a.src?0:2,g=void 0!==a.color?te(a.color):null,h=Eh.Zb(),l=h.get(e, +b,g);l||(l=new Fh(c,e,d,b,f,g),h.set(e,b,g,l));this.b=l;this.D=void 0!==a.offset?a.offset:[0,0];this.g=void 0!==a.offsetOrigin?a.offsetOrigin:"top-left";this.l=null;this.A=void 0!==a.size?a.size:null;Ch.call(this,{opacity:void 0!==a.opacity?a.opacity:1,rotation:void 0!==a.rotation?a.rotation:0,scale:void 0!==a.scale?a.scale:1,snapToPixel:void 0!==a.snapToPixel?a.snapToPixel:!0,rotateWithView:void 0!==a.rotateWithView?a.rotateWithView:!1})}y(Dh,Ch);k=Dh.prototype; +k.Yb=function(){if(this.f)return this.f;var a=this.c,b=this.Fb();if("fraction"==this.o||"fraction"==this.s){if(!b)return null;a=this.c.slice();"fraction"==this.o&&(a[0]*=b[0]);"fraction"==this.s&&(a[1]*=b[1])}if("top-left"!=this.a){if(!b)return null;a===this.c&&(a=this.c.slice());if("top-right"==this.a||"bottom-right"==this.a)a[0]=-a[0]+b[0];if("bottom-left"==this.a||"bottom-right"==this.a)a[1]=-a[1]+b[1]}return this.f=a};k.jc=function(){var a=this.b;return a.c?a.c:a.a};k.ld=function(){return this.b.g}; +k.td=function(){return this.b.f};k.pe=function(){var a=this.b;if(!a.o)if(a.s){var b=a.g[0],c=a.g[1],d=Oe(b,c);d.fillRect(0,0,b,c);a.o=d.canvas}else a.o=a.a;return a.o};k.Ia=function(){if(this.l)return this.l;var a=this.D;if("top-left"!=this.g){var b=this.Fb(),c=this.b.g;if(!b||!c)return null;a=a.slice();if("top-right"==this.g||"bottom-right"==this.g)a[0]=c[0]-b[0]-a[0];if("bottom-left"==this.g||"bottom-right"==this.g)a[1]=c[1]-b[1]-a[1]}return this.l=a};k.En=function(){return this.b.j}; +k.Fb=function(){return this.A?this.A:this.b.g};k.pf=function(a,b){return B(this.b,"change",a,b)};k.load=function(){this.b.load()};k.Xf=function(a,b){Qa(this.b,"change",a,b)};function Fh(a,b,c,d,e,f){$a.call(this);this.o=null;this.a=a?a:new Image;null!==d&&(this.a.crossOrigin=d);this.c=f?document.createElement("CANVAS"):null;this.l=f;this.i=null;this.f=e;this.g=c;this.j=b;this.s=!1;2==this.f&&Gh(this)}y(Fh,$a); +function Gh(a){var b=Oe(1,1);try{b.drawImage(a.a,0,0),b.getImageData(0,0,1,1)}catch(c){a.s=!0}}Fh.prototype.v=function(){this.f=3;this.i.forEach(Ka);this.i=null;this.b("change")}; +Fh.prototype.U=function(){this.f=2;this.g&&(this.a.width=this.g[0],this.a.height=this.g[1]);this.g=[this.a.width,this.a.height];this.i.forEach(Ka);this.i=null;Gh(this);if(!this.s&&null!==this.l){this.c.width=this.a.width;this.c.height=this.a.height;var a=this.c.getContext("2d");a.drawImage(this.a,0,0);for(var b=a.getImageData(0,0,this.a.width,this.a.height),c=b.data,d=this.l[0]/255,e=this.l[1]/255,f=this.l[2]/255,g=0,h=c.length;g<h;g+=4)c[g]*=d,c[g+1]*=e,c[g+2]*=f;a.putImageData(b,0,0)}this.b("change")}; +Fh.prototype.load=function(){if(0==this.f){this.f=1;this.i=[Pa(this.a,"error",this.v,this),Pa(this.a,"load",this.U,this)];try{this.a.src=this.j}catch(a){this.v()}}};function Eh(){this.b={};this.a=0}ba(Eh);Eh.prototype.clear=function(){this.b={};this.a=0};Eh.prototype.get=function(a,b,c){a=b+":"+a+":"+(c?ve(c):"null");return a in this.b?this.b[a]:null};Eh.prototype.set=function(a,b,c,d){this.b[b+":"+a+":"+(c?ve(c):"null")]=d;++this.a};function Hh(a,b){this.i=b;this.g={};this.s={}}y(Hh,Sa);function Ih(a){var b=a.viewState,c=a.coordinateToPixelMatrix;qh(c,a.size[0]/2,a.size[1]/2,1/b.resolution,-1/b.resolution,-b.rotation,-b.center[0],-b.center[1]);cd(c,a.pixelToCoordinateMatrix)}k=Hh.prototype;k.ka=function(){for(var a in this.g)Ta(this.g[a])};function Jh(){var a=Eh.Zb();if(32<a.a){var b=0,c,d;for(c in a.b)d=a.b[c],0!==(b++&3)||ab(d)||(delete a.b[c],--a.a)}} +k.ra=function(a,b,c,d,e,f){function g(a,e){var f=w(a).toString(),g=b.layerStates[w(e)].Qc;if(!(f in b.skippedFeatureUids)||g)return c.call(d,a,g?e:null)}var h,l=b.viewState,m=l.resolution,n=l.projection,l=a;if(n.a){var n=n.H(),p=ic(n),q=a[0];if(q<n[0]||q>n[2])l=[q+p*Math.ceil((n[0]-q)/p),a[1]]}n=b.layerStatesArray;for(p=n.length-1;0<=p;--p){var r=n[p],q=r.layer;if(nh(r,m)&&e.call(f,q)&&(r=Kh(this,q),q.ha()&&(h=r.ra(q.ha().D?l:a,b,g,d)),h))return h}}; +k.rh=function(a,b,c,d,e,f){var g,h=b.viewState.resolution,l=b.layerStatesArray,m;for(m=l.length-1;0<=m;--m){g=l[m];var n=g.layer;if(nh(g,h)&&e.call(f,n)&&(g=Kh(this,n).Cc(a,b,c,d)))return g}};k.sh=function(a,b,c,d){return void 0!==this.ra(a,b,qc,this,c,d)};function Kh(a,b){var c=w(b).toString();if(c in a.g)return a.g[c];var d=a.Xe(b);a.g[c]=d;a.s[c]=B(d,"change",a.Rk,a);return d}k.Rk=function(){this.i.render()};k.Ce=na; +k.To=function(a,b){for(var c in this.g)if(!(b&&c in b.layerStates)){var d=c,e=this.g[d];delete this.g[d];Ka(this.s[d]);delete this.s[d];Ta(e)}};function Lh(a,b){for(var c in a.g)if(!(c in b.layerStates)){b.postRenderFunctions.push(a.To.bind(a));break}}function rb(a,b){return a.zIndex-b.zIndex};function Mh(a,b){this.j=a;this.l=b;this.b=[];this.a=[];this.g={}}Mh.prototype.clear=function(){this.b.length=0;this.a.length=0;Fa(this.g)};function Nh(a){var b=a.b,c=a.a,d=b[0];1==b.length?(b.length=0,c.length=0):(b[0]=b.pop(),c[0]=c.pop(),Oh(a,0));b=a.l(d);delete a.g[b];return d}Mh.prototype.f=function(a){var b=this.j(a);return Infinity!=b?(this.b.push(a),this.a.push(b),this.g[this.l(a)]=!0,Ph(this,0,this.b.length-1),!0):!1};Mh.prototype.wc=function(){return this.b.length}; +Mh.prototype.Ya=function(){return 0===this.b.length};function Oh(a,b){for(var c=a.b,d=a.a,e=c.length,f=c[b],g=d[b],h=b;b<e>>1;){var l=2*b+1,m=2*b+2,l=m<e&&d[m]<d[l]?m:l;c[b]=c[l];d[b]=d[l];b=l}c[b]=f;d[b]=g;Ph(a,h,b)}function Ph(a,b,c){var d=a.b;a=a.a;for(var e=d[c],f=a[c];c>b;){var g=c-1>>1;if(a[g]>f)d[c]=d[g],a[c]=a[g],c=g;else break}d[c]=e;a[c]=f} +function Qh(a){var b=a.j,c=a.b,d=a.a,e=0,f=c.length,g,h,l;for(h=0;h<f;++h)g=c[h],l=b(g),Infinity==l?delete a.g[a.l(g)]:(d[e]=l,c[e++]=g);c.length=e;d.length=e;for(b=(a.b.length>>1)-1;0<=b;b--)Oh(a,b)};function Rh(a,b){Mh.call(this,function(b){return a.apply(null,b)},function(a){return a[0].ib()});this.s=b;this.i=0;this.c={}}y(Rh,Mh);Rh.prototype.f=function(a){var b=Mh.prototype.f.call(this,a);b&&B(a[0],"change",this.o,this);return b};Rh.prototype.o=function(a){a=a.target;var b=a.V();if(2===b||3===b||4===b||5===b)Qa(a,"change",this.o,this),a=a.ib(),a in this.c&&(delete this.c[a],--this.i),this.s()}; +function Sh(a,b,c){for(var d=0,e,f;a.i<b&&d<c&&0<a.wc();)e=Nh(a)[0],f=e.ib(),0!==e.V()||f in a.c||(a.c[f]=!0,++a.i,++d,e.load())};function Th(a,b,c){this.f=a;this.g=b;this.i=c;this.b=[];this.a=this.c=0}function Uh(a,b){var c=a.f,d=a.a,e=a.g-d,f=Math.log(a.g/a.a)/a.f;return ce({source:b,duration:f,easing:function(a){return d*(Math.exp(c*a*f)-1)/e}})};function Vh(a){eb.call(this);this.v=null;this.i(!0);this.handleEvent=a.handleEvent}y(Vh,eb);Vh.prototype.f=function(){return this.get("active")};Vh.prototype.l=function(){return this.v};Vh.prototype.i=function(a){this.set("active",a)};Vh.prototype.setMap=function(a){this.v=a};function Wh(a,b,c,d,e){if(void 0!==c){var f=b.La(),g=b.ab();void 0!==f&&g&&e&&0<e&&(a.Wa(de({rotation:f,duration:e,easing:Zd})),d&&a.Wa(ce({source:g,duration:e,easing:Zd})));b.rotate(c,d)}} +function Xh(a,b,c,d,e){var f=b.$();c=b.constrainResolution(f,c,0);Yh(a,b,c,d,e)}function Yh(a,b,c,d,e){if(c){var f=b.$(),g=b.ab();void 0!==f&&g&&c!==f&&e&&0<e&&(a.Wa(ee({resolution:f,duration:e,easing:Zd})),d&&a.Wa(ce({source:g,duration:e,easing:Zd})));if(d){var h;a=b.ab();e=b.$();void 0!==a&&void 0!==e&&(h=[d[0]-c*(d[0]-a[0])/e,d[1]-c*(d[1]-a[1])/e]);b.mb(h)}b.Ub(c)}};function Zh(a){a=a?a:{};this.a=a.delta?a.delta:1;Vh.call(this,{handleEvent:$h});this.c=void 0!==a.duration?a.duration:250}y(Zh,Vh);function $h(a){var b=!1,c=a.originalEvent;if(a.type==$g){var b=a.map,d=a.coordinate,c=c.shiftKey?-this.a:this.a,e=b.aa();Xh(b,e,c,d,this.c);a.preventDefault();b=!0}return!b};function ai(a){a=a.originalEvent;return a.altKey&&!(a.metaKey||a.ctrlKey)&&a.shiftKey}function bi(a){a=a.originalEvent;return 0==a.button&&!(He&&fg&&a.ctrlKey)}function ci(a){return"pointermove"==a.type}function di(a){return a.type==ah}function ei(a){a=a.originalEvent;return!a.altKey&&!(a.metaKey||a.ctrlKey)&&!a.shiftKey}function fi(a){a=a.originalEvent;return!a.altKey&&!(a.metaKey||a.ctrlKey)&&a.shiftKey} +function gi(a){a=a.originalEvent.target.tagName;return"INPUT"!==a&&"SELECT"!==a&&"TEXTAREA"!==a}function hi(a){return"mouse"==a.b.pointerType}function ii(a){a=a.b;return a.isPrimary&&0===a.button};function ji(a){a=a?a:{};Vh.call(this,{handleEvent:a.handleEvent?a.handleEvent:ki});this.Oe=a.handleDownEvent?a.handleDownEvent:rc;this.Pe=a.handleDragEvent?a.handleDragEvent:na;this.Mi=a.handleMoveEvent?a.handleMoveEvent:na;this.tj=a.handleUpEvent?a.handleUpEvent:rc;this.C=!1;this.ia={};this.o=[]}y(ji,Vh);function li(a){for(var b=a.length,c=0,d=0,e=0;e<b;e++)c+=a[e].clientX,d+=a[e].clientY;return[c/b,d/b]} +function ki(a){if(!(a instanceof Wg))return!0;var b=!1,c=a.type;if(c===eh||c===gh||c===ch)c=a.b,a.type==ch?delete this.ia[c.pointerId]:a.type==eh?this.ia[c.pointerId]=c:c.pointerId in this.ia&&(this.ia[c.pointerId]=c),this.o=Ga(this.ia);this.C&&(a.type==gh?this.Pe(a):a.type==ch&&(this.C=this.tj(a)));a.type==eh?(this.C=a=this.Oe(a),b=this.Gc(a)):a.type==fh&&this.Mi(a);return!b}ji.prototype.Gc=function(a){return a};function mi(a){ji.call(this,{handleDownEvent:ni,handleDragEvent:oi,handleUpEvent:pi});a=a?a:{};this.a=a.kinetic;this.c=this.j=null;this.A=a.condition?a.condition:ei;this.s=!1}y(mi,ji);function oi(a){var b=li(this.o);this.a&&this.a.b.push(b[0],b[1],Date.now());if(this.c){var c=this.c[0]-b[0],d=b[1]-this.c[1];a=a.map;var e=a.aa(),f=e.V(),d=c=[c,d],g=f.resolution;d[0]*=g;d[1]*=g;Gb(c,f.rotation);Bb(c,f.center);c=e.Pd(c);a.render();e.mb(c)}this.c=b} +function pi(a){a=a.map;var b=a.aa();if(0===this.o.length){var c;if(c=!this.s&&this.a)if(c=this.a,6>c.b.length)c=!1;else{var d=Date.now()-c.i,e=c.b.length-3;if(c.b[e+2]<d)c=!1;else{for(var f=e-3;0<f&&c.b[f+2]>d;)f-=3;var d=c.b[e+2]-c.b[f+2],g=c.b[e]-c.b[f],e=c.b[e+1]-c.b[f+1];c.c=Math.atan2(e,g);c.a=Math.sqrt(g*g+e*e)/d;c=c.a>c.g}}c&&(c=this.a,c=(c.g-c.a)/c.f,e=this.a.c,f=b.ab(),this.j=Uh(this.a,f),a.Wa(this.j),f=a.Ga(f),c=a.Ma([f[0]-c*Math.cos(e),f[1]-c*Math.sin(e)]),c=b.Pd(c),b.mb(c));Xd(b,-1);a.render(); +return!1}this.c=null;return!0}function ni(a){if(0<this.o.length&&this.A(a)){var b=a.map,c=b.aa();this.c=null;this.C||Xd(c,1);b.render();this.j&&nb(b.R,this.j)&&(c.mb(a.frameState.viewState.center),this.j=null);this.a&&(a=this.a,a.b.length=0,a.c=0,a.a=0);this.s=1<this.o.length;return!0}return!1}mi.prototype.Gc=rc;function qi(a){a=a?a:{};ji.call(this,{handleDownEvent:ri,handleDragEvent:si,handleUpEvent:ti});this.c=a.condition?a.condition:ai;this.a=void 0;this.j=void 0!==a.duration?a.duration:250}y(qi,ji);function si(a){if(hi(a)){var b=a.map,c=b.Za();a=a.pixel;c=Math.atan2(c[1]/2-a[1],a[0]-c[0]/2);if(void 0!==this.a){a=c-this.a;var d=b.aa(),e=d.La();b.render();Wh(b,d,e-a)}this.a=c}} +function ti(a){if(!hi(a))return!0;a=a.map;var b=a.aa();Xd(b,-1);var c=b.La(),d=this.j,c=b.constrainRotation(c,0);Wh(a,b,c,void 0,d);return!1}function ri(a){return hi(a)&&bi(a)&&this.c(a)?(a=a.map,Xd(a.aa(),1),a.render(),this.a=void 0,!0):!1}qi.prototype.Gc=rc;function ui(a){this.f=null;this.a=document.createElement("div");this.a.style.position="absolute";this.a.className="ol-box "+a;this.g=this.c=this.b=null}y(ui,Sa);ui.prototype.ka=function(){this.setMap(null)};function vi(a){var b=a.c,c=a.g;a=a.a.style;a.left=Math.min(b[0],c[0])+"px";a.top=Math.min(b[1],c[1])+"px";a.width=Math.abs(c[0]-b[0])+"px";a.height=Math.abs(c[1]-b[1])+"px"} +ui.prototype.setMap=function(a){if(this.b){this.b.A.removeChild(this.a);var b=this.a.style;b.left=b.top=b.width=b.height="inherit"}(this.b=a)&&this.b.A.appendChild(this.a)};function wi(a){var b=a.c,c=a.g,b=[b,[b[0],c[1]],c,[c[0],b[1]]].map(a.b.Ma,a.b);b[4]=b[0].slice();a.f?a.f.pa([b]):a.f=new E([b])}ui.prototype.W=function(){return this.f};function xi(a,b,c){Wa.call(this,a);this.coordinate=b;this.mapBrowserEvent=c}y(xi,Wa);function yi(a){ji.call(this,{handleDownEvent:zi,handleDragEvent:Ai,handleUpEvent:Bi});a=a?a:{};this.a=new ui(a.className||"ol-dragbox");this.c=null;this.D=a.condition?a.condition:qc;this.A=a.boxEndCondition?a.boxEndCondition:Ci}y(yi,ji);function Ci(a,b,c){a=c[0]-b[0];b=c[1]-b[1];return 64<=a*a+b*b} +function Ai(a){if(hi(a)){var b=this.a,c=a.pixel;b.c=this.c;b.g=c;wi(b);vi(b);this.b(new xi("boxdrag",a.coordinate,a))}}yi.prototype.W=function(){return this.a.W()};yi.prototype.s=na;function Bi(a){if(!hi(a))return!0;this.a.setMap(null);this.A(a,this.c,a.pixel)&&(this.s(a),this.b(new xi("boxend",a.coordinate,a)));return!1} +function zi(a){if(hi(a)&&bi(a)&&this.D(a)){this.c=a.pixel;this.a.setMap(a.map);var b=this.a,c=this.c;b.c=this.c;b.g=c;wi(b);vi(b);this.b(new xi("boxstart",a.coordinate,a));return!0}return!1};function Di(a){a=a?a:{};var b=a.condition?a.condition:fi;this.j=void 0!==a.duration?a.duration:200;this.R=void 0!==a.out?a.out:!1;yi.call(this,{condition:b,className:a.className||"ol-dragzoom"})}y(Di,yi); +Di.prototype.s=function(){var a=this.v,b=a.aa(),c=a.Za(),d=this.W().H();if(this.R){var e=b.Kc(c),d=[a.Ga(cc(d)),a.Ga(ec(d))],f=Wb(Infinity,Infinity,-Infinity,-Infinity,void 0),g,h;g=0;for(h=d.length;g<h;++g)Mb(f,d[g]);oc(e,1/Td(f,c));d=e}c=b.constrainResolution(Td(d,c));e=b.$();f=b.ab();a.Wa(ee({resolution:e,duration:this.j,easing:Zd}));a.Wa(ce({source:f,duration:this.j,easing:Zd}));b.mb(kc(d));b.Ub(c)};function Ei(a){Vh.call(this,{handleEvent:Fi});a=a||{};this.a=function(a){return ei(a)&&gi(a)};this.c=void 0!==a.condition?a.condition:this.a;this.o=void 0!==a.duration?a.duration:100;this.j=void 0!==a.pixelDelta?a.pixelDelta:128}y(Ei,Vh); +function Fi(a){var b=!1;if("keydown"==a.type){var c=a.originalEvent.keyCode;if(this.c(a)&&(40==c||37==c||39==c||38==c)){var d=a.map,b=d.aa(),e=b.$()*this.j,f=0,g=0;40==c?g=-e:37==c?f=-e:39==c?f=e:g=e;c=[f,g];Gb(c,b.La());e=this.o;if(f=b.ab())e&&0<e&&d.Wa(ce({source:f,duration:e,easing:ae})),d=b.Pd([f[0]+c[0],f[1]+c[1]]),b.mb(d);a.preventDefault();b=!0}}return!b};function Gi(a){Vh.call(this,{handleEvent:Hi});a=a?a:{};this.c=a.condition?a.condition:gi;this.a=a.delta?a.delta:1;this.o=void 0!==a.duration?a.duration:100}y(Gi,Vh);function Hi(a){var b=!1;if("keydown"==a.type||"keypress"==a.type){var c=a.originalEvent.charCode;if(this.c(a)&&(43==c||45==c)){b=a.map;c=43==c?this.a:-this.a;b.render();var d=b.aa();Xh(b,d,c,void 0,this.o);a.preventDefault();b=!0}}return!b};function Ii(a){Vh.call(this,{handleEvent:Ji});a=a||{};this.c=0;this.C=void 0!==a.duration?a.duration:250;this.s=void 0!==a.useAnchor?a.useAnchor:!0;this.a=null;this.j=this.o=void 0}y(Ii,Vh); +function Ji(a){var b=!1;if("wheel"==a.type||"mousewheel"==a.type){var b=a.map,c=a.originalEvent;this.s&&(this.a=a.coordinate);var d;"wheel"==a.type?(d=c.deltaY,dg&&c.deltaMode===pa.WheelEvent.DOM_DELTA_PIXEL&&(d/=gg),c.deltaMode===pa.WheelEvent.DOM_DELTA_LINE&&(d*=40)):"mousewheel"==a.type&&(d=-c.wheelDeltaY,eg&&(d/=3));this.c+=d;void 0===this.o&&(this.o=Date.now());d=Math.max(80-(Date.now()-this.o),0);pa.clearTimeout(this.j);this.j=pa.setTimeout(this.A.bind(this,b),d);a.preventDefault();b=!0}return!b} +Ii.prototype.A=function(a){var b=sa(this.c,-1,1),c=a.aa();a.render();Xh(a,c,-b,this.a,this.C);this.c=0;this.a=null;this.j=this.o=void 0};Ii.prototype.D=function(a){this.s=a;a||(this.a=null)};function Ki(a){ji.call(this,{handleDownEvent:Li,handleDragEvent:Mi,handleUpEvent:Ni});a=a||{};this.c=null;this.j=void 0;this.a=!1;this.s=0;this.D=void 0!==a.threshold?a.threshold:.3;this.A=void 0!==a.duration?a.duration:250}y(Ki,ji); +function Mi(a){var b=0,c=this.o[0],d=this.o[1],c=Math.atan2(d.clientY-c.clientY,d.clientX-c.clientX);void 0!==this.j&&(b=c-this.j,this.s+=b,!this.a&&Math.abs(this.s)>this.D&&(this.a=!0));this.j=c;a=a.map;c=a.a.getBoundingClientRect();d=li(this.o);d[0]-=c.left;d[1]-=c.top;this.c=a.Ma(d);this.a&&(c=a.aa(),d=c.La(),a.render(),Wh(a,c,d+b,this.c))} +function Ni(a){if(2>this.o.length){a=a.map;var b=a.aa();Xd(b,-1);if(this.a){var c=b.La(),d=this.c,e=this.A,c=b.constrainRotation(c,0);Wh(a,b,c,d,e)}return!1}return!0}function Li(a){return 2<=this.o.length?(a=a.map,this.c=null,this.j=void 0,this.a=!1,this.s=0,this.C||Xd(a.aa(),1),a.render(),!0):!1}Ki.prototype.Gc=rc;function Oi(a){ji.call(this,{handleDownEvent:Pi,handleDragEvent:Qi,handleUpEvent:Ri});a=a?a:{};this.c=null;this.s=void 0!==a.duration?a.duration:400;this.a=void 0;this.j=1}y(Oi,ji);function Qi(a){var b=1,c=this.o[0],d=this.o[1],e=c.clientX-d.clientX,c=c.clientY-d.clientY,e=Math.sqrt(e*e+c*c);void 0!==this.a&&(b=this.a/e);this.a=e;1!=b&&(this.j=b);a=a.map;var e=a.aa(),c=e.$(),d=a.a.getBoundingClientRect(),f=li(this.o);f[0]-=d.left;f[1]-=d.top;this.c=a.Ma(f);a.render();Yh(a,e,c*b,this.c)} +function Ri(a){if(2>this.o.length){a=a.map;var b=a.aa();Xd(b,-1);var c=b.$(),d=this.c,e=this.s,c=b.constrainResolution(c,0,this.j-1);Yh(a,b,c,d,e);return!1}return!0}function Pi(a){return 2<=this.o.length?(a=a.map,this.c=null,this.a=void 0,this.j=1,this.C||Xd(a.aa(),1),a.render(),!0):!1}Oi.prototype.Gc=rc;function Si(a){a=a?a:{};var b=new le,c=new Th(-.005,.05,100);(void 0!==a.altShiftDragRotate?a.altShiftDragRotate:1)&&b.push(new qi);(void 0!==a.doubleClickZoom?a.doubleClickZoom:1)&&b.push(new Zh({delta:a.zoomDelta,duration:a.zoomDuration}));(void 0!==a.dragPan?a.dragPan:1)&&b.push(new mi({kinetic:c}));(void 0!==a.pinchRotate?a.pinchRotate:1)&&b.push(new Ki);(void 0!==a.pinchZoom?a.pinchZoom:1)&&b.push(new Oi({duration:a.zoomDuration}));if(void 0!==a.keyboard?a.keyboard:1)b.push(new Ei),b.push(new Gi({delta:a.zoomDelta, +duration:a.zoomDuration}));(void 0!==a.mouseWheelZoom?a.mouseWheelZoom:1)&&b.push(new Ii({duration:a.zoomDuration}));(void 0!==a.shiftDragZoom?a.shiftDragZoom:1)&&b.push(new Di({duration:a.zoomDuration}));return b};function Ti(a){var b=a||{};a=Ea({},b);delete a.layers;b=b.layers;ih.call(this,a);this.f=[];this.a={};B(this,gb("layers"),this.Tk,this);b?Array.isArray(b)&&(b=new le(b.slice())):b=new le;this.oh(b)}y(Ti,ih);k=Ti.prototype;k.ce=function(){this.xb()&&this.u()}; +k.Tk=function(){this.f.forEach(Ka);this.f.length=0;var a=this.Tc();this.f.push(B(a,"add",this.Sk,this),B(a,"remove",this.Uk,this));for(var b in this.a)this.a[b].forEach(Ka);Fa(this.a);var a=a.a,c,d;b=0;for(c=a.length;b<c;b++)d=a[b],this.a[w(d).toString()]=[B(d,"propertychange",this.ce,this),B(d,"change",this.ce,this)];this.u()};k.Sk=function(a){a=a.element;var b=w(a).toString();this.a[b]=[B(a,"propertychange",this.ce,this),B(a,"change",this.ce,this)];this.u()}; +k.Uk=function(a){a=w(a.element).toString();this.a[a].forEach(Ka);delete this.a[a];this.u()};k.Tc=function(){return this.get("layers")};k.oh=function(a){this.set("layers",a)}; +k.hf=function(a){var b=void 0!==a?a:[],c=b.length;this.Tc().forEach(function(a){a.hf(b)});a=jh(this);var d,e;for(d=b.length;c<d;c++)e=b[c],e.opacity*=a.opacity,e.visible=e.visible&&a.visible,e.maxResolution=Math.min(e.maxResolution,a.maxResolution),e.minResolution=Math.max(e.minResolution,a.minResolution),void 0!==a.extent&&(e.extent=void 0!==e.extent?mc(e.extent,a.extent):a.extent);return b};k.kf=function(){return"ready"};function Ui(a){vc.call(this,{code:a,units:"m",extent:Vi,global:!0,worldExtent:Wi})}y(Ui,vc);Ui.prototype.getPointResolution=function(a,b){return a/ta(b[1]/6378137)};var Xi=6378137*Math.PI,Vi=[-Xi,-Xi,Xi,Xi],Wi=[-180,-85,180,85],Hc="EPSG:3857 EPSG:102100 EPSG:102113 EPSG:900913 urn:ogc:def:crs:EPSG:6.18:3:3857 urn:ogc:def:crs:EPSG::3857 http://www.opengis.net/gml/srs/epsg.xml#3857".split(" ").map(function(a){return new Ui(a)}); +function Ic(a,b,c){var d=a.length;c=1<c?c:2;void 0===b&&(2<c?b=a.slice():b=Array(d));for(var e=0;e<d;e+=c)b[e]=6378137*Math.PI*a[e]/180,b[e+1]=6378137*Math.log(Math.tan(Math.PI*(a[e+1]+90)/360));return b}function Jc(a,b,c){var d=a.length;c=1<c?c:2;void 0===b&&(2<c?b=a.slice():b=Array(d));for(var e=0;e<d;e+=c)b[e]=180*a[e]/(6378137*Math.PI),b[e+1]=360*Math.atan(Math.exp(a[e+1]/6378137))/Math.PI-90;return b};var Yi=new sc(6378137);function Zi(a,b){vc.call(this,{code:a,units:"degrees",extent:$i,axisOrientation:b,global:!0,metersPerUnit:aj,worldExtent:$i})}y(Zi,vc);Zi.prototype.getPointResolution=function(a){return a}; +var $i=[-180,-90,180,90],aj=Math.PI*Yi.radius/180,Kc=[new Zi("CRS:84"),new Zi("EPSG:4326","neu"),new Zi("urn:ogc:def:crs:EPSG::4326","neu"),new Zi("urn:ogc:def:crs:EPSG:6.6:4326","neu"),new Zi("urn:ogc:def:crs:OGC:1.3:CRS84"),new Zi("urn:ogc:def:crs:OGC:2:84"),new Zi("http://www.opengis.net/gml/srs/epsg.xml#4326","neu"),new Zi("urn:x-ogc:def:crs:EPSG:4326","neu")];function bj(){zc(Hc);zc(Kc);Gc()};function cj(a){mh.call(this,a?a:{})}y(cj,mh);function dj(a){a=a?a:{};var b=Ea({},a);delete b.preload;delete b.useInterimTilesOnError;mh.call(this,b);this.l(void 0!==a.preload?a.preload:0);this.A(void 0!==a.useInterimTilesOnError?a.useInterimTilesOnError:!0)}y(dj,mh);dj.prototype.f=function(){return this.get("preload")};dj.prototype.l=function(a){this.set("preload",a)};dj.prototype.c=function(){return this.get("useInterimTilesOnError")};dj.prototype.A=function(a){this.set("useInterimTilesOnError",a)};var ej=[0,0,0,1],fj=[],gj=[0,0,0,1];function hj(a,b,c,d){0!==b&&(a.translate(c,d),a.rotate(b),a.translate(-c,-d))};function ij(a){a=a||{};this.b=void 0!==a.color?a.color:null;this.a=void 0}ij.prototype.g=function(){return this.b};ij.prototype.f=function(a){this.b=a;this.a=void 0};function jj(a){void 0===a.a&&(a.a=a.b instanceof CanvasPattern||a.b instanceof CanvasGradient?w(a.b).toString():"f"+(a.b?ve(a.b):"-"));return a.a};function kj(){this.a=-1};function lj(){this.a=64;this.b=Array(4);this.c=Array(this.a);this.b[0]=1732584193;this.b[1]=4023233417;this.b[2]=2562383102;this.b[3]=271733878;this.f=this.g=0}(function(){function a(){}a.prototype=kj.prototype;lj.a=kj.prototype;lj.prototype=new a;lj.prototype.constructor=lj;lj.b=function(a,c,d){for(var e=Array(arguments.length-2),f=2;f<arguments.length;f++)e[f-2]=arguments[f];return kj.prototype[c].apply(a,e)}})(); +function mj(a,b,c){c||(c=0);var d=Array(16);if(da(b))for(var e=0;16>e;++e)d[e]=b.charCodeAt(c++)|b.charCodeAt(c++)<<8|b.charCodeAt(c++)<<16|b.charCodeAt(c++)<<24;else for(e=0;16>e;++e)d[e]=b[c++]|b[c++]<<8|b[c++]<<16|b[c++]<<24;b=a.b[0];c=a.b[1];var e=a.b[2],f=a.b[3],g;g=b+(f^c&(e^f))+d[0]+3614090360&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[1]+3905402710&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f&(b^c))+d[2]+606105819&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e&(f^b))+ +d[3]+3250441966&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(f^c&(e^f))+d[4]+4118548399&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[5]+1200080426&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f&(b^c))+d[6]+2821735955&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e&(f^b))+d[7]+4249261313&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(f^c&(e^f))+d[8]+1770035416&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[9]+2336552879&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f& +(b^c))+d[10]+4294925233&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e&(f^b))+d[11]+2304563134&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(f^c&(e^f))+d[12]+1804603682&4294967295;b=c+(g<<7&4294967295|g>>>25);g=f+(e^b&(c^e))+d[13]+4254626195&4294967295;f=b+(g<<12&4294967295|g>>>20);g=e+(c^f&(b^c))+d[14]+2792965006&4294967295;e=f+(g<<17&4294967295|g>>>15);g=c+(b^e&(f^b))+d[15]+1236535329&4294967295;c=e+(g<<22&4294967295|g>>>10);g=b+(e^f&(c^e))+d[1]+4129170786&4294967295;b=c+(g<<5&4294967295|g>>> +27);g=f+(c^e&(b^c))+d[6]+3225465664&4294967295;f=b+(g<<9&4294967295|g>>>23);g=e+(b^c&(f^b))+d[11]+643717713&4294967295;e=f+(g<<14&4294967295|g>>>18);g=c+(f^b&(e^f))+d[0]+3921069994&4294967295;c=e+(g<<20&4294967295|g>>>12);g=b+(e^f&(c^e))+d[5]+3593408605&4294967295;b=c+(g<<5&4294967295|g>>>27);g=f+(c^e&(b^c))+d[10]+38016083&4294967295;f=b+(g<<9&4294967295|g>>>23);g=e+(b^c&(f^b))+d[15]+3634488961&4294967295;e=f+(g<<14&4294967295|g>>>18);g=c+(f^b&(e^f))+d[4]+3889429448&4294967295;c=e+(g<<20&4294967295| +g>>>12);g=b+(e^f&(c^e))+d[9]+568446438&4294967295;b=c+(g<<5&4294967295|g>>>27);g=f+(c^e&(b^c))+d[14]+3275163606&4294967295;f=b+(g<<9&4294967295|g>>>23);g=e+(b^c&(f^b))+d[3]+4107603335&4294967295;e=f+(g<<14&4294967295|g>>>18);g=c+(f^b&(e^f))+d[8]+1163531501&4294967295;c=e+(g<<20&4294967295|g>>>12);g=b+(e^f&(c^e))+d[13]+2850285829&4294967295;b=c+(g<<5&4294967295|g>>>27);g=f+(c^e&(b^c))+d[2]+4243563512&4294967295;f=b+(g<<9&4294967295|g>>>23);g=e+(b^c&(f^b))+d[7]+1735328473&4294967295;e=f+(g<<14&4294967295| +g>>>18);g=c+(f^b&(e^f))+d[12]+2368359562&4294967295;c=e+(g<<20&4294967295|g>>>12);g=b+(c^e^f)+d[5]+4294588738&4294967295;b=c+(g<<4&4294967295|g>>>28);g=f+(b^c^e)+d[8]+2272392833&4294967295;f=b+(g<<11&4294967295|g>>>21);g=e+(f^b^c)+d[11]+1839030562&4294967295;e=f+(g<<16&4294967295|g>>>16);g=c+(e^f^b)+d[14]+4259657740&4294967295;c=e+(g<<23&4294967295|g>>>9);g=b+(c^e^f)+d[1]+2763975236&4294967295;b=c+(g<<4&4294967295|g>>>28);g=f+(b^c^e)+d[4]+1272893353&4294967295;f=b+(g<<11&4294967295|g>>>21);g=e+(f^ +b^c)+d[7]+4139469664&4294967295;e=f+(g<<16&4294967295|g>>>16);g=c+(e^f^b)+d[10]+3200236656&4294967295;c=e+(g<<23&4294967295|g>>>9);g=b+(c^e^f)+d[13]+681279174&4294967295;b=c+(g<<4&4294967295|g>>>28);g=f+(b^c^e)+d[0]+3936430074&4294967295;f=b+(g<<11&4294967295|g>>>21);g=e+(f^b^c)+d[3]+3572445317&4294967295;e=f+(g<<16&4294967295|g>>>16);g=c+(e^f^b)+d[6]+76029189&4294967295;c=e+(g<<23&4294967295|g>>>9);g=b+(c^e^f)+d[9]+3654602809&4294967295;b=c+(g<<4&4294967295|g>>>28);g=f+(b^c^e)+d[12]+3873151461&4294967295; +f=b+(g<<11&4294967295|g>>>21);g=e+(f^b^c)+d[15]+530742520&4294967295;e=f+(g<<16&4294967295|g>>>16);g=c+(e^f^b)+d[2]+3299628645&4294967295;c=e+(g<<23&4294967295|g>>>9);g=b+(e^(c|~f))+d[0]+4096336452&4294967295;b=c+(g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[7]+1126891415&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[14]+2878612391&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[5]+4237533241&4294967295;c=e+(g<<21&4294967295|g>>>11);g=b+(e^(c|~f))+d[12]+1700485571&4294967295;b=c+ +(g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[3]+2399980690&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[10]+4293915773&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[1]+2240044497&4294967295;c=e+(g<<21&4294967295|g>>>11);g=b+(e^(c|~f))+d[8]+1873313359&4294967295;b=c+(g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[15]+4264355552&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[6]+2734768916&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[13]+1309151649&4294967295; +c=e+(g<<21&4294967295|g>>>11);g=b+(e^(c|~f))+d[4]+4149444226&4294967295;b=c+(g<<6&4294967295|g>>>26);g=f+(c^(b|~e))+d[11]+3174756917&4294967295;f=b+(g<<10&4294967295|g>>>22);g=e+(b^(f|~c))+d[2]+718787259&4294967295;e=f+(g<<15&4294967295|g>>>17);g=c+(f^(e|~b))+d[9]+3951481745&4294967295;a.b[0]=a.b[0]+b&4294967295;a.b[1]=a.b[1]+(e+(g<<21&4294967295|g>>>11))&4294967295;a.b[2]=a.b[2]+e&4294967295;a.b[3]=a.b[3]+f&4294967295} +function nj(a,b){var c;void 0===c&&(c=b.length);for(var d=c-a.a,e=a.c,f=a.g,g=0;g<c;){if(0==f)for(;g<=d;)mj(a,b,g),g+=a.a;if(da(b))for(;g<c;){if(e[f++]=b.charCodeAt(g++),f==a.a){mj(a,e);f=0;break}}else for(;g<c;)if(e[f++]=b[g++],f==a.a){mj(a,e);f=0;break}}a.g=f;a.f+=c};function oj(a){a=a||{};this.b=void 0!==a.color?a.color:null;this.f=a.lineCap;this.g=void 0!==a.lineDash?a.lineDash:null;this.c=a.lineJoin;this.i=a.miterLimit;this.a=a.width;this.l=void 0}k=oj.prototype;k.Kn=function(){return this.b};k.dk=function(){return this.f};k.Ln=function(){return this.g};k.ek=function(){return this.c};k.jk=function(){return this.i};k.Mn=function(){return this.a};k.Nn=function(a){this.b=a;this.l=void 0};k.fp=function(a){this.f=a;this.l=void 0}; +k.On=function(a){this.g=a;this.l=void 0};k.gp=function(a){this.c=a;this.l=void 0};k.hp=function(a){this.i=a;this.l=void 0};k.lp=function(a){this.a=a;this.l=void 0}; +function pj(a){if(void 0===a.l){var b="s"+(a.b?ve(a.b):"-")+","+(void 0!==a.f?a.f.toString():"-")+","+(a.g?a.g.toString():"-")+","+(void 0!==a.c?a.c:"-")+","+(void 0!==a.i?a.i.toString():"-")+","+(void 0!==a.a?a.a.toString():"-"),c=new lj;nj(c,b);b=Array((56>c.g?c.a:2*c.a)-c.g);b[0]=128;for(var d=1;d<b.length-8;++d)b[d]=0;for(var e=8*c.f,d=b.length-8;d<b.length;++d)b[d]=e&255,e/=256;nj(c,b);b=Array(16);for(d=e=0;4>d;++d)for(var f=0;32>f;f+=8)b[e++]=c.b[d]>>>f&255;if(8192>=b.length)c=String.fromCharCode.apply(null, +b);else for(c="",d=0;d<b.length;d+=8192)e=pe(b,d,d+8192),c+=String.fromCharCode.apply(null,e);a.l=c}return a.l};function qj(a){a=a||{};this.l=this.f=this.c=null;this.g=void 0!==a.fill?a.fill:null;this.b=void 0!==a.stroke?a.stroke:null;this.a=a.radius;this.A=[0,0];this.s=this.D=this.o=null;var b=a.atlasManager,c,d=null,e,f=0;this.b&&(e=ve(this.b.b),f=this.b.a,void 0===f&&(f=1),d=this.b.g,hg||(d=null));var g=2*(this.a+f)+1;e={strokeStyle:e,Bd:f,size:g,lineDash:d};if(void 0===b)b=Oe(g,g),this.f=b.canvas,c=g=this.f.width,this.Gh(e,b,0,0),this.g?this.l=this.f:(b=Oe(e.size,e.size),this.l=b.canvas,this.Fh(e,b,0,0)); +else{g=Math.round(g);(d=!this.g)&&(c=this.Fh.bind(this,e));var f=this.b?pj(this.b):"-",h=this.g?jj(this.g):"-";this.c&&f==this.c[1]&&h==this.c[2]&&this.a==this.c[3]||(this.c=["c"+f+h+(void 0!==this.a?this.a.toString():"-"),f,h,this.a]);b=b.add(this.c[0],g,g,this.Gh.bind(this,e),c);this.f=b.image;this.A=[b.offsetX,b.offsetY];c=b.image.width;this.l=d?b.Sg:this.f}this.o=[g/2,g/2];this.D=[g,g];this.s=[c,c];Ch.call(this,{opacity:1,rotateWithView:!1,rotation:0,scale:1,snapToPixel:void 0!==a.snapToPixel? +a.snapToPixel:!0})}y(qj,Ch);k=qj.prototype;k.Yb=function(){return this.o};k.Bn=function(){return this.g};k.pe=function(){return this.l};k.jc=function(){return this.f};k.td=function(){return 2};k.ld=function(){return this.s};k.Ia=function(){return this.A};k.Cn=function(){return this.a};k.Fb=function(){return this.D};k.Dn=function(){return this.b};k.pf=na;k.load=na;k.Xf=na; +k.Gh=function(a,b,c,d){b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();b.arc(a.size/2,a.size/2,this.a,0,2*Math.PI,!0);this.g&&(b.fillStyle=xe(this.g.b),b.fill());this.b&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.Bd,a.lineDash&&b.setLineDash(a.lineDash),b.stroke());b.closePath()}; +k.Fh=function(a,b,c,d){b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();b.arc(a.size/2,a.size/2,this.a,0,2*Math.PI,!0);b.fillStyle=ve(ej);b.fill();this.b&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.Bd,a.lineDash&&b.setLineDash(a.lineDash),b.stroke());b.closePath()};function rj(a){a=a||{};this.i=null;this.g=sj;void 0!==a.geometry&&this.Jh(a.geometry);this.c=void 0!==a.fill?a.fill:null;this.a=void 0!==a.image?a.image:null;this.f=void 0!==a.stroke?a.stroke:null;this.l=void 0!==a.text?a.text:null;this.b=a.zIndex}k=rj.prototype;k.W=function(){return this.i};k.Zj=function(){return this.g};k.Pn=function(){return this.c};k.Qn=function(){return this.a};k.Rn=function(){return this.f};k.Ha=function(){return this.l};k.Sn=function(){return this.b}; +k.Jh=function(a){"function"===typeof a?this.g=a:"string"===typeof a?this.g=function(b){return b.get(a)}:a?a&&(this.g=function(){return a}):this.g=sj;this.i=a};k.Tn=function(a){this.b=a};function tj(a){if("function"!==typeof a){var b;b=Array.isArray(a)?a:[a];a=function(){return b}}return a}var uj=null;function vj(){if(!uj){var a=new ij({color:"rgba(255,255,255,0.4)"}),b=new oj({color:"#3399CC",width:1.25});uj=[new rj({image:new qj({fill:a,stroke:b,radius:5}),fill:a,stroke:b})]}return uj} +function wj(){var a={},b=[255,255,255,1],c=[0,153,255,1];a.Polygon=[new rj({fill:new ij({color:[255,255,255,.5]})})];a.MultiPolygon=a.Polygon;a.LineString=[new rj({stroke:new oj({color:b,width:5})}),new rj({stroke:new oj({color:c,width:3})})];a.MultiLineString=a.LineString;a.Circle=a.Polygon.concat(a.LineString);a.Point=[new rj({image:new qj({radius:6,fill:new ij({color:c}),stroke:new oj({color:b,width:1.5})}),zIndex:Infinity})];a.MultiPoint=a.Point;a.GeometryCollection=a.Polygon.concat(a.LineString, +a.Point);return a}function sj(a){return a.W()};function G(a){a=a?a:{};var b=Ea({},a);delete b.style;delete b.renderBuffer;delete b.updateWhileAnimating;delete b.updateWhileInteracting;mh.call(this,b);this.a=void 0!==a.renderBuffer?a.renderBuffer:100;this.A=null;this.i=void 0;this.l(a.style);this.S=void 0!==a.updateWhileAnimating?a.updateWhileAnimating:!1;this.T=void 0!==a.updateWhileInteracting?a.updateWhileInteracting:!1}y(G,mh);function xj(a){return a.get("renderOrder")}G.prototype.C=function(){return this.A};G.prototype.D=function(){return this.i}; +G.prototype.l=function(a){this.A=void 0!==a?a:vj;this.i=null===a?void 0:tj(this.A);this.u()};function I(a){a=a?a:{};var b=Ea({},a);delete b.preload;delete b.useInterimTilesOnError;G.call(this,b);this.Y(a.preload?a.preload:0);this.ia(a.useInterimTilesOnError?a.useInterimTilesOnError:!0);this.s=a.renderMode||"hybrid"}y(I,G);I.prototype.f=function(){return this.get("preload")};I.prototype.c=function(){return this.get("useInterimTilesOnError")};I.prototype.Y=function(a){this.set("preload",a)};I.prototype.ia=function(a){this.set("useInterimTilesOnError",a)};function yj(a,b,c,d,e){this.f=a;this.A=b;this.c=c;this.D=d;this.Hc=e;this.i=this.b=this.a=this.ia=this.Ra=this.Y=null;this.qa=this.za=this.v=this.Ba=this.ya=this.R=0;this.Gb=!1;this.l=this.ta=0;this.Aa=!1;this.S=0;this.g="";this.j=this.C=this.qb=this.Sa=0;this.T=this.s=this.o=null;this.U=[];this.Hb=Xc()}y(yj,kh); +function zj(a,b,c){if(a.i){b=gd(b,0,c,2,a.D,a.U);c=a.f;var d=a.Hb,e=c.globalAlpha;1!=a.v&&(c.globalAlpha=e*a.v);var f=a.ta;a.Gb&&(f+=a.Hc);var g,h;g=0;for(h=b.length;g<h;g+=2){var l=b[g]-a.R,m=b[g+1]-a.ya;a.Aa&&(l=Math.round(l),m=Math.round(m));if(0!==f||1!=a.l){var n=l+a.R,p=m+a.ya;qh(d,n,p,a.l,a.l,f,-n,-p);c.setTransform(d[0],d[1],d[4],d[5],d[12],d[13])}c.drawImage(a.i,a.za,a.qa,a.S,a.Ba,l,m,a.S,a.Ba)}0===f&&1==a.l||c.setTransform(1,0,0,1,0,0);1!=a.v&&(c.globalAlpha=e)}} +function Aj(a,b,c,d){var e=0;if(a.T&&""!==a.g){a.o&&Bj(a,a.o);a.s&&Cj(a,a.s);var f=a.T,g=a.f,h=a.ia;h?(h.font!=f.font&&(h.font=g.font=f.font),h.textAlign!=f.textAlign&&(h.textAlign=g.textAlign=f.textAlign),h.textBaseline!=f.textBaseline&&(h.textBaseline=g.textBaseline=f.textBaseline)):(g.font=f.font,g.textAlign=f.textAlign,g.textBaseline=f.textBaseline,a.ia={font:f.font,textAlign:f.textAlign,textBaseline:f.textBaseline});b=gd(b,e,c,d,a.D,a.U);for(f=a.f;e<c;e+=d){g=b[e]+a.Sa;h=b[e+1]+a.qb;if(0!==a.C|| +1!=a.j){var l=qh(a.Hb,g,h,a.j,a.j,a.C,-g,-h);f.setTransform(l[0],l[1],l[4],l[5],l[12],l[13])}a.s&&f.strokeText(a.g,g,h);a.o&&f.fillText(a.g,g,h)}0===a.C&&1==a.j||f.setTransform(1,0,0,1,0,0)}}function Dj(a,b,c,d,e,f){var g=a.f;a=gd(b,c,d,e,a.D,a.U);g.moveTo(a[0],a[1]);b=a.length;f&&(b-=2);for(c=2;c<b;c+=2)g.lineTo(a[c],a[c+1]);f&&g.closePath();return d}function Ej(a,b,c,d,e){var f,g;f=0;for(g=d.length;f<g;++f)c=Dj(a,b,c,d[f],e,!0);return c}k=yj.prototype; +k.Rd=function(a){if(nc(this.c,a.H())){if(this.a||this.b){this.a&&Bj(this,this.a);this.b&&Cj(this,this.b);var b;b=this.D;var c=this.U,d=a.la();b=d?gd(d,0,d.length,a.va(),b,c):null;c=b[2]-b[0];d=b[3]-b[1];c=Math.sqrt(c*c+d*d);d=this.f;d.beginPath();d.arc(b[0],b[1],c,0,2*Math.PI);this.a&&d.fill();this.b&&d.stroke()}""!==this.g&&Aj(this,a.rd(),2,2)}};k.sd=function(a){this.Sb(a.c,a.f);this.Tb(a.a);this.Vb(a.Ha())}; +k.sc=function(a){switch(a.X()){case "Point":this.uc(a);break;case "LineString":this.hd(a);break;case "Polygon":this.bf(a);break;case "MultiPoint":this.tc(a);break;case "MultiLineString":this.$e(a);break;case "MultiPolygon":this.af(a);break;case "GeometryCollection":this.Ze(a);break;case "Circle":this.Rd(a)}};k.Ye=function(a,b){var c=(0,b.g)(a);c&&nc(this.c,c.H())&&(this.sd(b),this.sc(c))};k.Ze=function(a){a=a.c;var b,c;b=0;for(c=a.length;b<c;++b)this.sc(a[b])}; +k.uc=function(a){var b=a.la();a=a.va();this.i&&zj(this,b,b.length);""!==this.g&&Aj(this,b,b.length,a)};k.tc=function(a){var b=a.la();a=a.va();this.i&&zj(this,b,b.length);""!==this.g&&Aj(this,b,b.length,a)};k.hd=function(a){if(nc(this.c,a.H())){if(this.b){Cj(this,this.b);var b=this.f,c=a.la();b.beginPath();Dj(this,c,0,c.length,a.va(),!1);b.stroke()}""!==this.g&&(a=Fj(a),Aj(this,a,2,2))}}; +k.$e=function(a){var b=a.H();if(nc(this.c,b)){if(this.b){Cj(this,this.b);var b=this.f,c=a.la(),d=0,e=a.Db(),f=a.va();b.beginPath();var g,h;g=0;for(h=e.length;g<h;++g)d=Dj(this,c,d,e[g],f,!1);b.stroke()}""!==this.g&&(a=Gj(a),Aj(this,a,a.length,2))}};k.bf=function(a){if(nc(this.c,a.H())){if(this.b||this.a){this.a&&Bj(this,this.a);this.b&&Cj(this,this.b);var b=this.f;b.beginPath();Ej(this,a.Mb(),0,a.Db(),a.va());this.a&&b.fill();this.b&&b.stroke()}""!==this.g&&(a=Md(a),Aj(this,a,2,2))}}; +k.af=function(a){if(nc(this.c,a.H())){if(this.b||this.a){this.a&&Bj(this,this.a);this.b&&Cj(this,this.b);var b=this.f,c=Hj(a),d=0,e=a.i,f=a.va(),g,h;g=0;for(h=e.length;g<h;++g){var l=e[g];b.beginPath();d=Ej(this,c,d,l,f);this.a&&b.fill();this.b&&b.stroke()}}""!==this.g&&(a=Ij(a),Aj(this,a,a.length,2))}};function Bj(a,b){var c=a.f,d=a.Y;d?d.fillStyle!=b.fillStyle&&(d.fillStyle=c.fillStyle=b.fillStyle):(c.fillStyle=b.fillStyle,a.Y={fillStyle:b.fillStyle})} +function Cj(a,b){var c=a.f,d=a.Ra;d?(d.lineCap!=b.lineCap&&(d.lineCap=c.lineCap=b.lineCap),hg&&!pb(d.lineDash,b.lineDash)&&c.setLineDash(d.lineDash=b.lineDash),d.lineJoin!=b.lineJoin&&(d.lineJoin=c.lineJoin=b.lineJoin),d.lineWidth!=b.lineWidth&&(d.lineWidth=c.lineWidth=b.lineWidth),d.miterLimit!=b.miterLimit&&(d.miterLimit=c.miterLimit=b.miterLimit),d.strokeStyle!=b.strokeStyle&&(d.strokeStyle=c.strokeStyle=b.strokeStyle)):(c.lineCap=b.lineCap,hg&&c.setLineDash(b.lineDash),c.lineJoin=b.lineJoin,c.lineWidth= +b.lineWidth,c.miterLimit=b.miterLimit,c.strokeStyle=b.strokeStyle,a.Ra={lineCap:b.lineCap,lineDash:b.lineDash,lineJoin:b.lineJoin,lineWidth:b.lineWidth,miterLimit:b.miterLimit,strokeStyle:b.strokeStyle})} +k.Sb=function(a,b){if(a){var c=a.b;this.a={fillStyle:xe(c?c:ej)}}else this.a=null;if(b){var c=b.b,d=b.f,e=b.g,f=b.c,g=b.a,h=b.i;this.b={lineCap:void 0!==d?d:"round",lineDash:e?e:fj,lineJoin:void 0!==f?f:"round",lineWidth:this.A*(void 0!==g?g:1),miterLimit:void 0!==h?h:10,strokeStyle:ve(c?c:gj)}}else this.b=null}; +k.Tb=function(a){if(a){var b=a.Yb(),c=a.jc(1),d=a.Ia(),e=a.Fb();this.R=b[0];this.ya=b[1];this.Ba=e[1];this.i=c;this.v=a.v;this.za=d[0];this.qa=d[1];this.Gb=a.U;this.ta=a.j;this.l=a.i;this.Aa=a.C;this.S=e[0]}else this.i=null}; +k.Vb=function(a){if(a){var b=a.b;b?(b=b.b,this.o={fillStyle:xe(b?b:ej)}):this.o=null;var c=a.l;if(c){var b=c.b,d=c.f,e=c.g,f=c.c,g=c.a,c=c.i;this.s={lineCap:void 0!==d?d:"round",lineDash:e?e:fj,lineJoin:void 0!==f?f:"round",lineWidth:void 0!==g?g:1,miterLimit:void 0!==c?c:10,strokeStyle:ve(b?b:gj)}}else this.s=null;var b=a.g,d=a.f,e=a.c,f=a.i,g=a.a,c=a.Ha(),h=a.o;a=a.j;this.T={font:void 0!==b?b:"10px sans-serif",textAlign:void 0!==h?h:"center",textBaseline:void 0!==a?a:"middle"};this.g=void 0!==c? +c:"";this.Sa=void 0!==d?this.A*d:0;this.qb=void 0!==e?this.A*e:0;this.C=void 0!==f?f:0;this.j=this.A*(void 0!==g?g:1)}else this.g=""};function Jj(a){th.call(this,a);this.R=Xc()}y(Jj,th); +Jj.prototype.i=function(a,b,c){Kj(this,"precompose",c,a,void 0);var d=this.f?this.f.a():null;if(d){var e=b.extent,f=void 0!==e;if(f){var g=a.pixelRatio,h=a.size[0]*g,l=a.size[1]*g,m=a.viewState.rotation,n=fc(e),p=ec(e),q=dc(e),e=cc(e);sh(a.coordinateToPixelMatrix,n,n);sh(a.coordinateToPixelMatrix,p,p);sh(a.coordinateToPixelMatrix,q,q);sh(a.coordinateToPixelMatrix,e,e);c.save();hj(c,-m,h/2,l/2);c.beginPath();c.moveTo(n[0]*g,n[1]*g);c.lineTo(p[0]*g,p[1]*g);c.lineTo(q[0]*g,q[1]*g);c.lineTo(e[0]*g,e[1]* +g);c.clip();hj(c,m,h/2,l/2)}g=this.s;h=c.globalAlpha;c.globalAlpha=b.opacity;c.drawImage(d,0,0,+d.width,+d.height,Math.round(g[12]),Math.round(g[13]),Math.round(d.width*g[0]),Math.round(d.height*g[5]));c.globalAlpha=h;f&&c.restore()}Lj(this,c,a)}; +function Kj(a,b,c,d,e){var f=a.a;if(ab(f,b)){var g=d.size[0]*d.pixelRatio,h=d.size[1]*d.pixelRatio,l=d.viewState.rotation;hj(c,-l,g/2,h/2);a=void 0!==e?e:Mj(a,d,0);a=new yj(c,d.pixelRatio,d.extent,a,d.viewState.rotation);f.b(new lh(b,f,a,d,c,null));hj(c,l,g/2,h/2)}}function Lj(a,b,c,d){Kj(a,"postcompose",b,c,d)}function Mj(a,b,c){var d=b.viewState,e=b.pixelRatio;return qh(a.R,e*b.size[0]/2,e*b.size[1]/2,e/d.resolution,-e/d.resolution,-d.rotation,-d.center[0]+c,-d.center[1])};var Nj=["Polygon","LineString","Image","Text"];function Oj(a,b,c){this.qa=a;this.T=b;this.f=null;this.c=0;this.resolution=c;this.Ba=this.ya=null;this.a=[];this.coordinates=[];this.Ra=Xc();this.b=[];this.Y=[];this.ia=Xc();this.za=Xc()}y(Oj,kh); +function Pj(a,b,c,d,e,f){var g=a.coordinates.length,h=a.df(),l=[b[c],b[c+1]],m=[NaN,NaN],n=!0,p,q,r;for(p=c+e;p<d;p+=e)m[0]=b[p],m[1]=b[p+1],r=Vb(h,m),r!==q?(n&&(a.coordinates[g++]=l[0],a.coordinates[g++]=l[1]),a.coordinates[g++]=m[0],a.coordinates[g++]=m[1],n=!1):1===r?(a.coordinates[g++]=m[0],a.coordinates[g++]=m[1],n=!1):n=!0,l[0]=m[0],l[1]=m[1],q=r;p===c+e&&(a.coordinates[g++]=l[0],a.coordinates[g++]=l[1]);f&&(a.coordinates[g++]=b[c],a.coordinates[g++]=b[c+1]);return g} +function Qj(a,b){a.ya=[0,b,0];a.a.push(a.ya);a.Ba=[0,b,0];a.b.push(a.Ba)} +function Rj(a,b,c,d,e,f,g,h,l){var m;rh(d,a.Ra)?m=a.Y:(m=gd(a.coordinates,0,a.coordinates.length,2,d,a.Y),$c(a.Ra,d));d=!Ha(f);var n=0,p=g.length,q,r,u=a.ia;a=a.za;for(var x,v,D,A;n<p;){var z=g[n],F,N,K,X;switch(z[0]){case 0:q=z[1];d&&f[w(q).toString()]||!q.W()?n=z[2]:void 0===l||nc(l,q.W().H())?++n:n=z[2];break;case 1:b.beginPath();++n;break;case 2:q=z[1];r=m[q];z=m[q+1];D=m[q+2]-r;q=m[q+3]-z;b.arc(r,z,Math.sqrt(D*D+q*q),0,2*Math.PI,!0);++n;break;case 3:b.closePath();++n;break;case 4:q=z[1];r=z[2]; +F=z[3];K=z[4]*c;var oa=z[5]*c,H=z[6];N=z[7];var ya=z[8],Ua=z[9];D=z[11];A=z[12];var Xa=z[13],Va=z[14];for(z[10]&&(D+=e);q<r;q+=2){z=m[q]-K;X=m[q+1]-oa;Xa&&(z=Math.round(z),X=Math.round(X));if(1!=A||0!==D){var Aa=z+K,Qb=X+oa;qh(u,Aa,Qb,A,A,D,-Aa,-Qb);b.transform(u[0],u[1],u[4],u[5],u[12],u[13])}Aa=b.globalAlpha;1!=N&&(b.globalAlpha=Aa*N);var Qb=Va+ya>F.width?F.width-ya:Va,Nb=H+Ua>F.height?F.height-Ua:H;b.drawImage(F,ya,Ua,Qb,Nb,z,X,Qb*c,Nb*c);1!=N&&(b.globalAlpha=Aa);if(1!=A||0!==D)cd(u,a),b.transform(a[0], +a[1],a[4],a[5],a[12],a[13])}++n;break;case 5:q=z[1];r=z[2];K=z[3];oa=z[4]*c;H=z[5]*c;D=z[6];A=z[7]*c;F=z[8];for(N=z[9];q<r;q+=2){z=m[q]+oa;X=m[q+1]+H;if(1!=A||0!==D)qh(u,z,X,A,A,D,-z,-X),b.transform(u[0],u[1],u[4],u[5],u[12],u[13]);ya=K.split("\n");Ua=ya.length;1<Ua?(Xa=Math.round(1.5*b.measureText("M").width),X-=(Ua-1)/2*Xa):Xa=0;for(Va=0;Va<Ua;Va++)Aa=ya[Va],N&&b.strokeText(Aa,z,X),F&&b.fillText(Aa,z,X),X+=Xa;if(1!=A||0!==D)cd(u,a),b.transform(a[0],a[1],a[4],a[5],a[12],a[13])}++n;break;case 6:if(void 0!== +h&&(q=z[1],q=h(q)))return q;++n;break;case 7:b.fill();++n;break;case 8:q=z[1];r=z[2];z=m[q];X=m[q+1];D=z+.5|0;A=X+.5|0;if(D!==x||A!==v)b.moveTo(z,X),x=D,v=A;for(q+=2;q<r;q+=2)if(z=m[q],X=m[q+1],D=z+.5|0,A=X+.5|0,q==r-2||D!==x||A!==v)b.lineTo(z,X),x=D,v=A;++n;break;case 9:b.fillStyle=z[1];++n;break;case 10:x=void 0!==z[7]?z[7]:!0;v=z[2];b.strokeStyle=z[1];b.lineWidth=x?v*c:v;b.lineCap=z[3];b.lineJoin=z[4];b.miterLimit=z[5];hg&&b.setLineDash(z[6]);v=x=NaN;++n;break;case 11:b.font=z[1];b.textAlign=z[2]; +b.textBaseline=z[3];++n;break;case 12:b.stroke();++n;break;default:++n}}}Oj.prototype.Pa=function(a,b,c,d,e){Rj(this,a,b,c,d,e,this.a,void 0)};function Sj(a){var b=a.b;b.reverse();var c,d=b.length,e,f,g=-1;for(c=0;c<d;++c)if(e=b[c],f=e[0],6==f)g=c;else if(0==f){e[2]=c;e=a.b;for(f=c;g<f;){var h=e[g];e[g]=e[f];e[f]=h;++g;--f}g=-1}}function Tj(a,b){a.ya[2]=a.a.length;a.ya=null;a.Ba[2]=a.b.length;a.Ba=null;var c=[6,b];a.a.push(c);a.b.push(c)}Oj.prototype.ke=na;Oj.prototype.df=function(){return this.T}; +function Uj(a,b,c){Oj.call(this,a,b,c);this.o=this.S=null;this.R=this.D=this.C=this.A=this.U=this.v=this.s=this.j=this.l=this.i=this.g=void 0}y(Uj,Oj);Uj.prototype.uc=function(a,b){if(this.o){Qj(this,b);var c=a.la(),d=this.coordinates.length,c=Pj(this,c,0,c.length,a.va(),!1);this.a.push([4,d,c,this.o,this.g,this.i,this.l,this.j,this.s,this.v,this.U,this.A,this.C,this.D,this.R]);this.b.push([4,d,c,this.S,this.g,this.i,this.l,this.j,this.s,this.v,this.U,this.A,this.C,this.D,this.R]);Tj(this,b)}}; +Uj.prototype.tc=function(a,b){if(this.o){Qj(this,b);var c=a.la(),d=this.coordinates.length,c=Pj(this,c,0,c.length,a.va(),!1);this.a.push([4,d,c,this.o,this.g,this.i,this.l,this.j,this.s,this.v,this.U,this.A,this.C,this.D,this.R]);this.b.push([4,d,c,this.S,this.g,this.i,this.l,this.j,this.s,this.v,this.U,this.A,this.C,this.D,this.R]);Tj(this,b)}};Uj.prototype.ke=function(){Sj(this);this.i=this.g=void 0;this.o=this.S=null;this.R=this.D=this.A=this.U=this.v=this.s=this.j=this.C=this.l=void 0}; +Uj.prototype.Tb=function(a){var b=a.Yb(),c=a.Fb(),d=a.pe(1),e=a.jc(1),f=a.Ia();this.g=b[0];this.i=b[1];this.S=d;this.o=e;this.l=c[1];this.j=a.v;this.s=f[0];this.v=f[1];this.U=a.U;this.A=a.j;this.C=a.i;this.D=a.C;this.R=c[0]};function Vj(a,b,c){Oj.call(this,a,b,c);this.g={fd:void 0,ad:void 0,bd:null,cd:void 0,dd:void 0,ed:void 0,nf:0,strokeStyle:void 0,lineCap:void 0,lineDash:null,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0}}y(Vj,Oj); +function Wj(a,b,c,d,e){var f=a.coordinates.length;b=Pj(a,b,c,d,e,!1);f=[8,f,b];a.a.push(f);a.b.push(f);return d}k=Vj.prototype;k.df=function(){this.f||(this.f=Pb(this.T),0<this.c&&Ob(this.f,this.resolution*(this.c+1)/2,this.f));return this.f}; +function Xj(a){var b=a.g,c=b.strokeStyle,d=b.lineCap,e=b.lineDash,f=b.lineJoin,g=b.lineWidth,h=b.miterLimit;b.fd==c&&b.ad==d&&pb(b.bd,e)&&b.cd==f&&b.dd==g&&b.ed==h||(b.nf!=a.coordinates.length&&(a.a.push([12]),b.nf=a.coordinates.length),a.a.push([10,c,g,d,f,h,e],[1]),b.fd=c,b.ad=d,b.bd=e,b.cd=f,b.dd=g,b.ed=h)} +k.hd=function(a,b){var c=this.g,d=c.lineWidth;void 0!==c.strokeStyle&&void 0!==d&&(Xj(this),Qj(this,b),this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash],[1]),c=a.la(),Wj(this,c,0,c.length,a.va()),this.b.push([12]),Tj(this,b))}; +k.$e=function(a,b){var c=this.g,d=c.lineWidth;if(void 0!==c.strokeStyle&&void 0!==d){Xj(this);Qj(this,b);this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash],[1]);var c=a.Db(),d=a.la(),e=a.va(),f=0,g,h;g=0;for(h=c.length;g<h;++g)f=Wj(this,d,f,c[g],e);this.b.push([12]);Tj(this,b)}};k.ke=function(){this.g.nf!=this.coordinates.length&&this.a.push([12]);Sj(this);this.g=null}; +k.Sb=function(a,b){var c=b.b;this.g.strokeStyle=ve(c?c:gj);c=b.f;this.g.lineCap=void 0!==c?c:"round";c=b.g;this.g.lineDash=c?c:fj;c=b.c;this.g.lineJoin=void 0!==c?c:"round";c=b.a;this.g.lineWidth=void 0!==c?c:1;c=b.i;this.g.miterLimit=void 0!==c?c:10;this.g.lineWidth>this.c&&(this.c=this.g.lineWidth,this.f=null)}; +function Yj(a,b,c){Oj.call(this,a,b,c);this.g={ug:void 0,fd:void 0,ad:void 0,bd:null,cd:void 0,dd:void 0,ed:void 0,fillStyle:void 0,strokeStyle:void 0,lineCap:void 0,lineDash:null,lineJoin:void 0,lineWidth:void 0,miterLimit:void 0}}y(Yj,Oj); +function Zj(a,b,c,d,e){var f=a.g,g=[1];a.a.push(g);a.b.push(g);var h,g=0;for(h=d.length;g<h;++g){var l=d[g],m=a.coordinates.length;c=Pj(a,b,c,l,e,!0);c=[8,m,c];m=[3];a.a.push(c,m);a.b.push(c,m);c=l}b=[7];a.b.push(b);void 0!==f.fillStyle&&a.a.push(b);void 0!==f.strokeStyle&&(f=[12],a.a.push(f),a.b.push(f));return c}k=Yj.prototype; +k.Rd=function(a,b){var c=this.g,d=c.strokeStyle;if(void 0!==c.fillStyle||void 0!==d){ak(this);Qj(this,b);this.b.push([9,ve(ej)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash]);var e=a.la(),d=this.coordinates.length;Pj(this,e,0,e.length,a.va(),!1);e=[1];d=[2,d];this.a.push(e,d);this.b.push(e,d);d=[7];this.b.push(d);void 0!==c.fillStyle&&this.a.push(d);void 0!==c.strokeStyle&&(c=[12],this.a.push(c),this.b.push(c));Tj(this,b)}}; +k.bf=function(a,b){var c=this.g,d=c.strokeStyle;if(void 0!==c.fillStyle||void 0!==d)ak(this),Qj(this,b),this.b.push([9,ve(ej)]),void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash]),c=a.Db(),d=a.Mb(),Zj(this,d,0,c,a.va()),Tj(this,b)}; +k.af=function(a,b){var c=this.g,d=c.strokeStyle;if(void 0!==c.fillStyle||void 0!==d){ak(this);Qj(this,b);this.b.push([9,ve(ej)]);void 0!==c.strokeStyle&&this.b.push([10,c.strokeStyle,c.lineWidth,c.lineCap,c.lineJoin,c.miterLimit,c.lineDash]);var c=a.i,d=Hj(a),e=a.va(),f=0,g,h;g=0;for(h=c.length;g<h;++g)f=Zj(this,d,f,c[g],e);Tj(this,b)}};k.ke=function(){Sj(this);this.g=null;var a=this.qa;if(0!==a){var b=this.coordinates,c,d;c=0;for(d=b.length;c<d;++c)b[c]=a*Math.round(b[c]/a)}}; +k.df=function(){this.f||(this.f=Pb(this.T),0<this.c&&Ob(this.f,this.resolution*(this.c+1)/2,this.f));return this.f}; +k.Sb=function(a,b){var c=this.g;if(a){var d=a.b;c.fillStyle=xe(d?d:ej)}else c.fillStyle=void 0;b?(d=b.b,c.strokeStyle=ve(d?d:gj),d=b.f,c.lineCap=void 0!==d?d:"round",d=b.g,c.lineDash=d?d.slice():fj,d=b.c,c.lineJoin=void 0!==d?d:"round",d=b.a,c.lineWidth=void 0!==d?d:1,d=b.i,c.miterLimit=void 0!==d?d:10,c.lineWidth>this.c&&(this.c=c.lineWidth,this.f=null)):(c.strokeStyle=void 0,c.lineCap=void 0,c.lineDash=null,c.lineJoin=void 0,c.lineWidth=void 0,c.miterLimit=void 0)}; +function ak(a){var b=a.g,c=b.fillStyle,d=b.strokeStyle,e=b.lineCap,f=b.lineDash,g=b.lineJoin,h=b.lineWidth,l=b.miterLimit;void 0!==c&&b.ug!=c&&(a.a.push([9,c]),b.ug=b.fillStyle);void 0===d||b.fd==d&&b.ad==e&&b.bd==f&&b.cd==g&&b.dd==h&&b.ed==l||(a.a.push([10,d,h,e,g,l,f]),b.fd=d,b.ad=e,b.bd=f,b.cd=g,b.dd=h,b.ed=l)}function bk(a,b,c){Oj.call(this,a,b,c);this.D=this.C=this.A=null;this.o="";this.U=this.v=this.s=this.j=0;this.l=this.i=this.g=null}y(bk,Oj); +function ck(a,b,c,d,e){if(""!==a.o&&a.l&&(a.g||a.i)){if(a.g){var f=a.g,g=a.A;if(!g||g.fillStyle!=f.fillStyle){var h=[9,f.fillStyle];a.a.push(h);a.b.push(h);g?g.fillStyle=f.fillStyle:a.A={fillStyle:f.fillStyle}}}a.i&&(f=a.i,g=a.C,g&&g.lineCap==f.lineCap&&g.lineDash==f.lineDash&&g.lineJoin==f.lineJoin&&g.lineWidth==f.lineWidth&&g.miterLimit==f.miterLimit&&g.strokeStyle==f.strokeStyle||(h=[10,f.strokeStyle,f.lineWidth,f.lineCap,f.lineJoin,f.miterLimit,f.lineDash,!1],a.a.push(h),a.b.push(h),g?(g.lineCap= +f.lineCap,g.lineDash=f.lineDash,g.lineJoin=f.lineJoin,g.lineWidth=f.lineWidth,g.miterLimit=f.miterLimit,g.strokeStyle=f.strokeStyle):a.C={lineCap:f.lineCap,lineDash:f.lineDash,lineJoin:f.lineJoin,lineWidth:f.lineWidth,miterLimit:f.miterLimit,strokeStyle:f.strokeStyle}));f=a.l;g=a.D;g&&g.font==f.font&&g.textAlign==f.textAlign&&g.textBaseline==f.textBaseline||(h=[11,f.font,f.textAlign,f.textBaseline],a.a.push(h),a.b.push(h),g?(g.font=f.font,g.textAlign=f.textAlign,g.textBaseline=f.textBaseline):a.D= +{font:f.font,textAlign:f.textAlign,textBaseline:f.textBaseline});Qj(a,e);f=a.coordinates.length;b=Pj(a,b,0,c,d,!1);b=[5,f,b,a.o,a.j,a.s,a.v,a.U,!!a.g,!!a.i];a.a.push(b);a.b.push(b);Tj(a,e)}} +bk.prototype.Vb=function(a){if(a){var b=a.b;b?(b=b.b,b=xe(b?b:ej),this.g?this.g.fillStyle=b:this.g={fillStyle:b}):this.g=null;var c=a.l;if(c){var b=c.b,d=c.f,e=c.g,f=c.c,g=c.a,c=c.i,d=void 0!==d?d:"round",e=e?e.slice():fj,f=void 0!==f?f:"round",g=void 0!==g?g:1,c=void 0!==c?c:10,b=ve(b?b:gj);if(this.i){var h=this.i;h.lineCap=d;h.lineDash=e;h.lineJoin=f;h.lineWidth=g;h.miterLimit=c;h.strokeStyle=b}else this.i={lineCap:d,lineDash:e,lineJoin:f,lineWidth:g,miterLimit:c,strokeStyle:b}}else this.i=null; +var l=a.g,b=a.f,d=a.c,e=a.i,g=a.a,c=a.Ha(),f=a.o,h=a.j;a=void 0!==l?l:"10px sans-serif";f=void 0!==f?f:"center";h=void 0!==h?h:"middle";this.l?(l=this.l,l.font=a,l.textAlign=f,l.textBaseline=h):this.l={font:a,textAlign:f,textBaseline:h};this.o=void 0!==c?c:"";this.j=void 0!==b?b:0;this.s=void 0!==d?d:0;this.v=void 0!==e?e:0;this.U=void 0!==g?g:1}else this.o=""};function dk(a,b,c,d){this.o=a;this.g=b;this.l=c;this.f=d;this.a={};this.c=Oe(1,1);this.i=Xc()} +function ek(a){for(var b in a.a){var c=a.a[b],d;for(d in c)c[d].ke()}}dk.prototype.ra=function(a,b,c,d,e){var f=this.i;qh(f,.5,.5,1/b,-1/b,-c,-a[0],-a[1]);var g=this.c;g.clearRect(0,0,1,1);var h;void 0!==this.f&&(h=Lb(),Mb(h,a),Ob(h,b*this.f,h));return fk(this,g,f,c,d,function(a){if(0<g.getImageData(0,0,1,1).data[3]){if(a=e(a))return a;g.clearRect(0,0,1,1)}},h)}; +dk.prototype.b=function(a,b){var c=void 0!==a?a.toString():"0",d=this.a[c];void 0===d&&(d={},this.a[c]=d);c=d[b];void 0===c&&(c=new gk[b](this.o,this.g,this.l),d[b]=c);return c};dk.prototype.Ya=function(){return Ha(this.a)}; +dk.prototype.Pa=function(a,b,c,d,e,f){var g=Object.keys(this.a).map(Number);g.sort(ib);var h=this.g,l=h[0],m=h[1],n=h[2],h=h[3],l=[l,m,l,h,n,h,n,m];gd(l,0,8,2,c,l);a.save();a.beginPath();a.moveTo(l[0],l[1]);a.lineTo(l[2],l[3]);a.lineTo(l[4],l[5]);a.lineTo(l[6],l[7]);a.closePath();a.clip();f=f?f:Nj;for(var p,q,l=0,m=g.length;l<m;++l)for(p=this.a[g[l].toString()],n=0,h=f.length;n<h;++n)q=p[f[n]],void 0!==q&&q.Pa(a,b,c,d,e);a.restore()}; +function fk(a,b,c,d,e,f,g){var h=Object.keys(a.a).map(Number);h.sort(function(a,b){return b-a});var l,m,n,p,q;l=0;for(m=h.length;l<m;++l)for(p=a.a[h[l].toString()],n=Nj.length-1;0<=n;--n)if(q=p[Nj[n]],void 0!==q&&(q=Rj(q,b,1,c,d,e,q.b,f,g)))return q}var gk={Image:Uj,LineString:Vj,Polygon:Yj,Text:bk};function hk(a,b,c,d){this.g=a;this.b=b;this.c=c;this.f=d}k=hk.prototype;k.get=function(a){return this.f[a]};k.Db=function(){return this.c};k.H=function(){this.a||(this.a="Point"===this.g?Xb(this.b):Yb(this.b,0,this.b.length,2));return this.a};k.Mb=function(){return this.b};k.la=hk.prototype.Mb;k.W=function(){return this};k.Nm=function(){return this.f};k.od=hk.prototype.W;k.va=function(){return 2};k.ec=na;k.X=function(){return this.g};function ik(a,b){return w(a)-w(b)}function jk(a,b){var c=.5*a/b;return c*c}function lk(a,b,c,d,e,f){var g=!1,h,l;if(h=c.a)l=h.td(),2==l||3==l?h.Xf(e,f):(0==l&&h.load(),h.pf(e,f),g=!0);if(e=(0,c.g)(b))d=e.od(d),(0,mk[d.X()])(a,d,c,b);return g} +var mk={Point:function(a,b,c,d){var e=c.a;if(e){if(2!=e.td())return;var f=a.b(c.b,"Image");f.Tb(e);f.uc(b,d)}if(e=c.Ha())a=a.b(c.b,"Text"),a.Vb(e),ck(a,b.la(),2,2,d)},LineString:function(a,b,c,d){var e=c.f;if(e){var f=a.b(c.b,"LineString");f.Sb(null,e);f.hd(b,d)}if(e=c.Ha())a=a.b(c.b,"Text"),a.Vb(e),ck(a,Fj(b),2,2,d)},Polygon:function(a,b,c,d){var e=c.c,f=c.f;if(e||f){var g=a.b(c.b,"Polygon");g.Sb(e,f);g.bf(b,d)}if(e=c.Ha())a=a.b(c.b,"Text"),a.Vb(e),ck(a,Md(b),2,2,d)},MultiPoint:function(a,b,c,d){var e= +c.a;if(e){if(2!=e.td())return;var f=a.b(c.b,"Image");f.Tb(e);f.tc(b,d)}if(e=c.Ha())a=a.b(c.b,"Text"),a.Vb(e),c=b.la(),ck(a,c,c.length,b.va(),d)},MultiLineString:function(a,b,c,d){var e=c.f;if(e){var f=a.b(c.b,"LineString");f.Sb(null,e);f.$e(b,d)}if(e=c.Ha())a=a.b(c.b,"Text"),a.Vb(e),b=Gj(b),ck(a,b,b.length,2,d)},MultiPolygon:function(a,b,c,d){var e=c.c,f=c.f;if(f||e){var g=a.b(c.b,"Polygon");g.Sb(e,f);g.af(b,d)}if(e=c.Ha())a=a.b(c.b,"Text"),a.Vb(e),b=Ij(b),ck(a,b,b.length,2,d)},GeometryCollection:function(a, +b,c,d){b=b.c;var e,f;e=0;for(f=b.length;e<f;++e)(0,mk[b[e].X()])(a,b[e],c,d)},Circle:function(a,b,c,d){var e=c.c,f=c.f;if(e||f){var g=a.b(c.b,"Polygon");g.Sb(e,f);g.Rd(b,d)}if(e=c.Ha())a=a.b(c.b,"Text"),a.Vb(e),ck(a,b.rd(),2,2,d)}};function nk(a,b,c,d,e,f){this.c=void 0!==f?f:null;oh.call(this,a,b,c,void 0!==f?0:2,d);this.g=e}y(nk,oh);nk.prototype.i=function(a){this.state=a?3:2;ph(this)};nk.prototype.load=function(){0==this.state&&(this.state=1,ph(this),this.c(this.i.bind(this)))};nk.prototype.a=function(){return this.g};var ok,pk=pa.navigator,qk=pa.chrome,rk=-1<pk.userAgent.indexOf("OPR"),sk=-1<pk.userAgent.indexOf("Edge");ok=!(!pk.userAgent.match("CriOS")&&null!==qk&&void 0!==qk&&"Google Inc."===pk.vendor&&0==rk&&0==sk);function tk(a,b,c,d){var e=Rc(c,b,a);c=b.getPointResolution(d,c);b=b.$b();void 0!==b&&(c*=b);b=a.$b();void 0!==b&&(c/=b);a=a.getPointResolution(c,e)/c;isFinite(a)&&0<a&&(c/=a);return c}function uk(a,b,c,d){a=c-a;b=d-b;var e=Math.sqrt(a*a+b*b);return[Math.round(c+a/e),Math.round(d+b/e)]} +function vk(a,b,c,d,e,f,g,h,l,m,n){var p=Oe(Math.round(c*a),Math.round(c*b));if(0===l.length)return p.canvas;p.scale(c,c);var q=Lb();l.forEach(function(a){ac(q,a.extent)});var r=Oe(Math.round(c*ic(q)/d),Math.round(c*jc(q)/d)),u=c/d;l.forEach(function(a){r.drawImage(a.image,m,m,a.image.width-2*m,a.image.height-2*m,(a.extent[0]-q[0])*u,-(a.extent[3]-q[3])*u,ic(a.extent)*u,jc(a.extent)*u)});var x=fc(g);h.f.forEach(function(a){var b=a.source,e=a.target,g=b[1][0],h=b[1][1],l=b[2][0],m=b[2][1];a=(e[0][0]- +x[0])/f;var n=-(e[0][1]-x[1])/f,u=(e[1][0]-x[0])/f,H=-(e[1][1]-x[1])/f,ya=(e[2][0]-x[0])/f,Ua=-(e[2][1]-x[1])/f,e=b[0][0],b=b[0][1],g=g-e,h=h-b,l=l-e,m=m-b;a:{g=[[g,h,0,0,u-a],[l,m,0,0,ya-a],[0,0,g,h,H-n],[0,0,l,m,Ua-n]];h=g.length;for(l=0;l<h;l++){for(var m=l,Xa=Math.abs(g[l][l]),Va=l+1;Va<h;Va++){var Aa=Math.abs(g[Va][l]);Aa>Xa&&(Xa=Aa,m=Va)}if(0===Xa){g=null;break a}Xa=g[m];g[m]=g[l];g[l]=Xa;for(m=l+1;m<h;m++)for(Xa=-g[m][l]/g[l][l],Va=l;Va<h+1;Va++)g[m][Va]=l==Va?0:g[m][Va]+Xa*g[l][Va]}l=Array(h); +for(m=h-1;0<=m;m--)for(l[m]=g[m][h]/g[m][m],Xa=m-1;0<=Xa;Xa--)g[Xa][h]-=g[Xa][m]*l[m];g=l}g&&(p.save(),p.beginPath(),ok?(l=(a+u+ya)/3,m=(n+H+Ua)/3,h=uk(l,m,a,n),u=uk(l,m,u,H),ya=uk(l,m,ya,Ua),p.moveTo(h[0],h[1]),p.lineTo(u[0],u[1]),p.lineTo(ya[0],ya[1])):(p.moveTo(a,n),p.lineTo(u,H),p.lineTo(ya,Ua)),p.closePath(),p.clip(),p.transform(g[0],g[2],g[1],g[3],a,n),p.translate(q[0]-e,q[3]-b),p.scale(d/c,-d/c),p.drawImage(r.canvas,0,0),p.restore())});n&&(p.save(),p.strokeStyle="black",p.lineWidth=1,h.f.forEach(function(a){var b= +a.target;a=(b[0][0]-x[0])/f;var c=-(b[0][1]-x[1])/f,d=(b[1][0]-x[0])/f,e=-(b[1][1]-x[1])/f,g=(b[2][0]-x[0])/f,b=-(b[2][1]-x[1])/f;p.beginPath();p.moveTo(a,c);p.lineTo(d,e);p.lineTo(g,b);p.closePath();p.stroke()}),p.restore());return p.canvas};function wk(a,b,c,d,e){this.g=a;this.c=b;var f={},g=Pc(this.c,this.g);this.a=function(a){var b=a[0]+"/"+a[1];f[b]||(f[b]=g(a));return f[b]};this.i=d;this.s=e*e;this.f=[];this.o=!1;this.j=this.g.a&&!!d&&!!this.g.H()&&ic(d)==ic(this.g.H());this.b=this.g.H()?ic(this.g.H()):null;this.l=this.c.H()?ic(this.c.H()):null;a=fc(c);b=ec(c);d=dc(c);c=cc(c);e=this.a(a);var h=this.a(b),l=this.a(d),m=this.a(c);xk(this,a,b,d,c,e,h,l,m,10);if(this.o){var n=Infinity;this.f.forEach(function(a){n=Math.min(n,a.source[0][0], +a.source[1][0],a.source[2][0])});this.f.forEach(function(a){if(Math.max(a.source[0][0],a.source[1][0],a.source[2][0])-n>this.b/2){var b=[[a.source[0][0],a.source[0][1]],[a.source[1][0],a.source[1][1]],[a.source[2][0],a.source[2][1]]];b[0][0]-n>this.b/2&&(b[0][0]-=this.b);b[1][0]-n>this.b/2&&(b[1][0]-=this.b);b[2][0]-n>this.b/2&&(b[2][0]-=this.b);Math.max(b[0][0],b[1][0],b[2][0])-Math.min(b[0][0],b[1][0],b[2][0])<this.b/2&&(a.source=b)}},this)}f={}} +function xk(a,b,c,d,e,f,g,h,l,m){var n=Kb([f,g,h,l]),p=a.b?ic(n)/a.b:null,q=a.g.a&&.5<p&&1>p,r=!1;if(0<m){if(a.c.g&&a.l)var u=Kb([b,c,d,e]),r=r|.25<ic(u)/a.l;!q&&a.g.g&&p&&(r|=.25<p)}if(r||!a.i||nc(n,a.i)){if(!(r||isFinite(f[0])&&isFinite(f[1])&&isFinite(g[0])&&isFinite(g[1])&&isFinite(h[0])&&isFinite(h[1])&&isFinite(l[0])&&isFinite(l[1])))if(0<m)r=!0;else return;if(0<m&&(r||(p=a.a([(b[0]+d[0])/2,(b[1]+d[1])/2]),n=q?(xa(f[0],a.b)+xa(h[0],a.b))/2-xa(p[0],a.b):(f[0]+h[0])/2-p[0],p=(f[1]+h[1])/2-p[1], +r=n*n+p*p>a.s),r)){Math.abs(b[0]-d[0])<=Math.abs(b[1]-d[1])?(q=[(c[0]+d[0])/2,(c[1]+d[1])/2],n=a.a(q),p=[(e[0]+b[0])/2,(e[1]+b[1])/2],r=a.a(p),xk(a,b,c,q,p,f,g,n,r,m-1),xk(a,p,q,d,e,r,n,h,l,m-1)):(q=[(b[0]+c[0])/2,(b[1]+c[1])/2],n=a.a(q),p=[(d[0]+e[0])/2,(d[1]+e[1])/2],r=a.a(p),xk(a,b,q,p,e,f,n,r,l,m-1),xk(a,q,c,d,p,n,g,h,r,m-1));return}if(q){if(!a.j)return;a.o=!0}a.f.push({source:[f,h,l],target:[b,d,e]});a.f.push({source:[f,g,h],target:[b,c,d]})}} +function yk(a){var b=Lb();a.f.forEach(function(a){a=a.source;Mb(b,a[0]);Mb(b,a[1]);Mb(b,a[2])});return b};function zk(a,b,c,d,e,f){this.v=b;this.s=a.H();var g=b.H(),h=g?mc(c,g):c,g=tk(a,b,kc(h),d);this.o=new wk(a,b,h,this.s,.5*g);this.c=d;this.g=c;a=yk(this.o);this.j=(this.ob=f(a,g,e))?this.ob.f:1;this.Ad=this.i=null;e=2;f=[];this.ob&&(e=0,f=this.ob.l);oh.call(this,c,d,this.j,e,f)}y(zk,oh);zk.prototype.ka=function(){1==this.state&&(Ka(this.Ad),this.Ad=null);oh.prototype.ka.call(this)};zk.prototype.a=function(){return this.i}; +zk.prototype.zd=function(){var a=this.ob.V();2==a&&(this.i=vk(ic(this.g)/this.c,jc(this.g)/this.c,this.j,this.ob.$(),0,this.c,this.g,this.o,[{extent:this.ob.H(),image:this.ob.a()}],0));this.state=a;ph(this)};zk.prototype.load=function(){if(0==this.state){this.state=1;ph(this);var a=this.ob.V();2==a||3==a?this.zd():(this.Ad=B(this.ob,"change",function(){var a=this.ob.V();if(2==a||3==a)Ka(this.Ad),this.Ad=null,this.zd()},this),this.ob.load())}};function Ak(a){jf.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,state:a.state});this.C=void 0!==a.resolutions?a.resolutions:null;this.a=null;this.qa=0}y(Ak,jf);function Bk(a,b){if(a.C){var c=kb(a.C,b,0);b=a.C[c]}return b} +Ak.prototype.A=function(a,b,c,d){var e=this.f;if(e&&d&&!Oc(e,d)){if(this.a){if(this.qa==this.g&&Oc(this.a.v,d)&&this.a.$()==b&&this.a.f==c&&$b(this.a.H(),a))return this.a;Ta(this.a);this.a=null}this.a=new zk(e,d,a,b,c,function(a,b,c){return this.Mc(a,b,c,e)}.bind(this));this.qa=this.g;return this.a}e&&(d=e);return this.Mc(a,b,c,d)};Ak.prototype.o=function(a){a=a.target;switch(a.V()){case 1:this.b(new Ck(Dk,a));break;case 2:this.b(new Ck(Ek,a));break;case 3:this.b(new Ck(Fk,a))}}; +function Gk(a,b){a.a().src=b}function Ck(a,b){Wa.call(this,a);this.image=b}y(Ck,Wa);var Dk="imageloadstart",Ek="imageloadend",Fk="imageloaderror";function Hk(a){Ak.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions,state:a.state});this.ia=a.canvasFunction;this.T=null;this.Y=0;this.ta=void 0!==a.ratio?a.ratio:1.5}y(Hk,Ak);Hk.prototype.Mc=function(a,b,c,d){b=Bk(this,b);var e=this.T;if(e&&this.Y==this.g&&e.$()==b&&e.f==c&&Ub(e.H(),a))return e;a=a.slice();oc(a,this.ta);(d=this.ia(a,b,c,[ic(a)/b*c,jc(a)/b*c],d))&&(e=new nk(a,b,c,this.l,d));this.T=e;this.Y=this.g;return e};function Ik(a){eb.call(this);this.i=void 0;this.a="geometry";this.c=null;this.l=void 0;this.f=null;B(this,gb(this.a),this.be,this);void 0!==a&&(a instanceof Tc||!a?this.Ua(a):this.G(a))}y(Ik,eb);k=Ik.prototype;k.clone=function(){var a=new Ik(this.O());a.Ec(this.a);var b=this.W();b&&a.Ua(b.clone());(b=this.c)&&a.sf(b);return a};k.W=function(){return this.get(this.a)};k.Xa=function(){return this.i};k.$j=function(){return this.a};k.Jl=function(){return this.c};k.ec=function(){return this.l};k.Kl=function(){this.u()}; +k.be=function(){this.f&&(Ka(this.f),this.f=null);var a=this.W();a&&(this.f=B(a,"change",this.Kl,this));this.u()};k.Ua=function(a){this.set(this.a,a)};k.sf=function(a){this.l=(this.c=a)?Jk(a):void 0;this.u()};k.mc=function(a){this.i=a;this.u()};k.Ec=function(a){Qa(this,gb(this.a),this.be,this);this.a=a;B(this,gb(this.a),this.be,this);this.be()};function Jk(a){if("function"!==typeof a){var b;b=Array.isArray(a)?a:[a];a=function(){return b}}return a};function Kk(a,b,c,d,e){df.call(this,a,b);this.g=Oe();this.l=d;this.c=null;this.f={gd:!1,Tf:null,bi:-1,Uf:-1,yd:null,ui:[]};this.v=e;this.j=c}y(Kk,df);k=Kk.prototype;k.$a=function(){return-1==this.f.Uf?null:this.g.canvas};k.Ul=function(){return this.l};k.ib=function(){return this.j};k.load=function(){0==this.state&&(this.state=1,ef(this),this.v(this,this.j),this.s(null,NaN,null))};k.gi=function(a){this.c=a;this.state=2;ef(this)};k.vf=function(a){this.o=a};k.ki=function(a){this.s=a};var Lk=document.implementation.createDocument("","",null);function Mk(a,b){return Lk.createElementNS(a,b)}function Nk(a,b){return Ok(a,b,[]).join("")}function Ok(a,b,c){if(a.nodeType==Node.CDATA_SECTION_NODE||a.nodeType==Node.TEXT_NODE)b?c.push(String(a.nodeValue).replace(/(\r\n|\r|\n)/g,"")):c.push(a.nodeValue);else for(a=a.firstChild;a;a=a.nextSibling)Ok(a,b,c);return c}function Pk(a){return a instanceof Document}function Qk(a){return a instanceof Node} +function Rk(a){return(new DOMParser).parseFromString(a,"application/xml")}function Sk(a,b){return function(c,d){var e=a.call(b,c,d);void 0!==e&&mb(d[d.length-1],e)}}function Tk(a,b){return function(c,d){var e=a.call(void 0!==b?b:this,c,d);void 0!==e&&d[d.length-1].push(e)}}function Uk(a,b){return function(c,d){var e=a.call(void 0!==b?b:this,c,d);void 0!==e&&(d[d.length-1]=e)}} +function Vk(a){return function(b,c){var d=a.call(this,b,c);if(void 0!==d){var e=c[c.length-1],f=b.localName,g;f in e?g=e[f]:g=e[f]=[];g.push(d)}}}function J(a,b){return function(c,d){var e=a.call(this,c,d);void 0!==e&&(d[d.length-1][void 0!==b?b:c.localName]=e)}}function L(a,b){return function(c,d,e){a.call(void 0!==b?b:this,c,d,e);e[e.length-1].node.appendChild(c)}} +function Wk(a){var b,c;return function(d,e,f){if(!b){b={};var g={};g[d.localName]=a;b[d.namespaceURI]=g;c=Xk(d.localName)}Yk(b,c,e,f)}}function Xk(a,b){return function(c,d,e){c=d[d.length-1].node;d=a;void 0===d&&(d=e);e=b;void 0===b&&(e=c.namespaceURI);return Mk(e,d)}}var Zk=Xk();function $k(a,b){for(var c=b.length,d=Array(c),e=0;e<c;++e)d[e]=a[b[e]];return d}function M(a,b,c){c=void 0!==c?c:{};var d,e;d=0;for(e=a.length;d<e;++d)c[a[d]]=b;return c} +function al(a,b,c,d){for(b=b.firstElementChild;b;b=b.nextElementSibling){var e=a[b.namespaceURI];void 0!==e&&(e=e[b.localName])&&e.call(d,b,c)}}function O(a,b,c,d,e){d.push(a);al(b,c,d,e);return d.pop()}function Yk(a,b,c,d,e,f){for(var g=(void 0!==e?e:c).length,h,l,m=0;m<g;++m)h=c[m],void 0!==h&&(l=b.call(f,h,d,void 0!==e?e[m]:void 0),void 0!==l&&a[l.namespaceURI][l.localName].call(f,l,h,d))}function bl(a,b,c,d,e,f,g){e.push(a);Yk(b,c,d,e,f,g);e.pop()};function cl(a,b,c,d){return function(e,f,g){var h=new XMLHttpRequest;h.open("GET","function"===typeof a?a(e,f,g):a,!0);"arraybuffer"==b.X()&&(h.responseType="arraybuffer");h.onload=function(){if(200<=h.status&&300>h.status){var a=b.X(),e;"json"==a||"text"==a?e=h.responseText:"xml"==a?(e=h.responseXML)||(e=Rk(h.responseText)):"arraybuffer"==a&&(e=h.response);e&&c.call(this,b.Fa(e,{featureProjection:g}),b.Oa(e))}else d.call(this)}.bind(this);h.send()}} +function dl(a,b){return cl(a,b,function(a,b){this.vf(b);this.gi(a)},function(){this.state=3;ef(this)})}function el(a,b){return cl(a,b,function(a){this.Jc(a)},na)};function fl(){return[[-Infinity,-Infinity,Infinity,Infinity]]};var gl,hl,il,jl; +(function(){var a={},b={ja:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ja=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Tp=c()}})(function(){return function d(a,b,g){function h(m,p){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!p&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ja:{}};a[m][0].call(q.ja,function(b){var d= +a[m][1][b];return h(d?d:b)},q,q.ja,d,a,b,g)}return b[m].ja}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b){function f(a,b,d,e,q){d=d||0;e=e||a.length-1;for(q=q||h;e>d;){if(600<e-d){var r=e-d+1,u=b-d+1,x=Math.log(r),v=.5*Math.exp(2*x/3),x=.5*Math.sqrt(x*v*(r-v)/r)*(0>u-r/2?-1:1);f(a,b,Math.max(d,Math.floor(b-u*v/r+x)),Math.min(e,Math.floor(b+(r-u)*v/r+x)),q)}r=a[b];u=d;v=e;g(a,d,b);for(0<q(a[e],r)&&g(a,d,e);u<v;){g(a,u,v);u++;for(v--;0>q(a[u],r);)u++; +for(;0<q(a[v],r);)v--}0===q(a[d],r)?g(a,d,v):(v++,g(a,v,e));v<=b&&(d=v+1);b<=v&&(e=v-1)}}function g(a,b,d){var e=a[b];a[b]=a[d];a[d]=e}function h(a,b){return a<b?-1:a>b?1:0}b.ja=f},{}],2:[function(a,b){function f(a,b){if(!(this instanceof f))return new f(a,b);this.Te=Math.max(4,a||9);this.hg=Math.max(2,Math.ceil(.4*this.Te));b&&this.mj(b);this.clear()}function g(a,b){h(a,0,a.children.length,b,a)}function h(a,b,d,e,f){f||(f=x(null));f.ca=Infinity;f.fa=Infinity;f.ea=-Infinity;f.ga=-Infinity;for(var g;b< +d;b++)g=a.children[b],l(f,a.Ta?e(g):g);return f}function l(a,b){a.ca=Math.min(a.ca,b.ca);a.fa=Math.min(a.fa,b.fa);a.ea=Math.max(a.ea,b.ea);a.ga=Math.max(a.ga,b.ga)}function m(a,b){return a.ca-b.ca}function n(a,b){return a.fa-b.fa}function p(a){return(a.ea-a.ca)*(a.ga-a.fa)}function q(a){return a.ea-a.ca+(a.ga-a.fa)}function r(a,b){return a.ca<=b.ca&&a.fa<=b.fa&&b.ea<=a.ea&&b.ga<=a.ga}function u(a,b){return b.ca<=a.ea&&b.fa<=a.ga&&b.ea>=a.ca&&b.ga>=a.fa}function x(a){return{children:a,height:1,Ta:!0, +ca:Infinity,fa:Infinity,ea:-Infinity,ga:-Infinity}}function v(a,b,d,e,f){for(var g=[b,d],h;g.length;)d=g.pop(),b=g.pop(),d-b<=e||(h=b+Math.ceil((d-b)/e/2)*e,D(a,h,b,d,f),g.push(b,h,h,d))}b.ja=f;var D=a("quickselect");f.prototype={all:function(){return this.cg(this.data,[])},search:function(a){var b=this.data,d=[],e=this.lb;if(!u(a,b))return d;for(var f=[],g,h,l,m;b;){g=0;for(h=b.children.length;g<h;g++)l=b.children[g],m=b.Ta?e(l):l,u(a,m)&&(b.Ta?d.push(l):r(a,m)?this.cg(l,d):f.push(l));b=f.pop()}return d}, +load:function(a){if(!a||!a.length)return this;if(a.length<this.hg){for(var b=0,d=a.length;b<d;b++)this.Ca(a[b]);return this}a=this.eg(a.slice(),0,a.length-1,0);this.data.children.length?this.data.height===a.height?this.jg(this.data,a):(this.data.height<a.height&&(b=this.data,this.data=a,a=b),this.gg(a,this.data.height-a.height-1,!0)):this.data=a;return this},Ca:function(a){a&&this.gg(a,this.data.height-1);return this},clear:function(){this.data=x([]);return this},remove:function(a,b){if(!a)return this; +for(var d=this.data,e=this.lb(a),f=[],g=[],h,l,m,n;d||f.length;){d||(d=f.pop(),l=f[f.length-1],h=g.pop(),n=!0);if(d.Ta){a:{m=a;var p=d.children,q=b;if(q){for(var u=0;u<p.length;u++)if(q(m,p[u])){m=u;break a}m=-1}else m=p.indexOf(m)}if(-1!==m){d.children.splice(m,1);f.push(d);this.kj(f);break}}n||d.Ta||!r(d,e)?l?(h++,d=l.children[h],n=!1):d=null:(f.push(d),g.push(h),h=0,l=d,d=d.children[0])}return this},lb:function(a){return a},Ve:m,We:n,toJSON:function(){return this.data},cg:function(a,b){for(var d= +[];a;)a.Ta?b.push.apply(b,a.children):d.push.apply(d,a.children),a=d.pop();return b},eg:function(a,b,d,e){var f=d-b+1,h=this.Te,l;if(f<=h)return l=x(a.slice(b,d+1)),g(l,this.lb),l;e||(e=Math.ceil(Math.log(f)/Math.log(h)),h=Math.ceil(f/Math.pow(h,e-1)));l=x([]);l.Ta=!1;l.height=e;var f=Math.ceil(f/h),h=f*Math.ceil(Math.sqrt(h)),m,n,p;for(v(a,b,d,h,this.Ve);b<=d;b+=h)for(n=Math.min(b+h-1,d),v(a,b,n,f,this.We),m=b;m<=n;m+=f)p=Math.min(m+f-1,n),l.children.push(this.eg(a,m,p,e-1));g(l,this.lb);return l}, +jj:function(a,b,d,e){for(var f,g,h,l,m,n,q,r;;){e.push(b);if(b.Ta||e.length-1===d)break;q=r=Infinity;f=0;for(g=b.children.length;f<g;f++)h=b.children[f],m=p(h),n=(Math.max(h.ea,a.ea)-Math.min(h.ca,a.ca))*(Math.max(h.ga,a.ga)-Math.min(h.fa,a.fa))-m,n<r?(r=n,q=m<q?m:q,l=h):n===r&&m<q&&(q=m,l=h);b=l||b.children[0]}return b},gg:function(a,b,d){var e=this.lb;d=d?a:e(a);var e=[],f=this.jj(d,this.data,b,e);f.children.push(a);for(l(f,d);0<=b;)if(e[b].children.length>this.Te)this.sj(e,b),b--;else break;this.gj(d, +e,b)},sj:function(a,b){var d=a[b],e=d.children.length,f=this.hg;this.hj(d,f,e);e=this.ij(d,f,e);e=x(d.children.splice(e,d.children.length-e));e.height=d.height;e.Ta=d.Ta;g(d,this.lb);g(e,this.lb);b?a[b-1].children.push(e):this.jg(d,e)},jg:function(a,b){this.data=x([a,b]);this.data.height=a.height+1;this.data.Ta=!1;g(this.data,this.lb)},ij:function(a,b,d){var e,f,g,l,m,n,q;m=n=Infinity;for(e=b;e<=d-b;e++)f=h(a,0,e,this.lb),g=h(a,e,d,this.lb),l=Math.max(0,Math.min(f.ea,g.ea)-Math.max(f.ca,g.ca))*Math.max(0, +Math.min(f.ga,g.ga)-Math.max(f.fa,g.fa)),f=p(f)+p(g),l<m?(m=l,q=e,n=f<n?f:n):l===m&&f<n&&(n=f,q=e);return q},hj:function(a,b,d){var e=a.Ta?this.Ve:m,f=a.Ta?this.We:n,g=this.dg(a,b,d,e);b=this.dg(a,b,d,f);g<b&&a.children.sort(e)},dg:function(a,b,d,e){a.children.sort(e);e=this.lb;var f=h(a,0,b,e),g=h(a,d-b,d,e),m=q(f)+q(g),n,p;for(n=b;n<d-b;n++)p=a.children[n],l(f,a.Ta?e(p):p),m+=q(f);for(n=d-b-1;n>=b;n--)p=a.children[n],l(g,a.Ta?e(p):p),m+=q(g);return m},gj:function(a,b,d){for(;0<=d;d--)l(b[d],a)}, +kj:function(a){for(var b=a.length-1,d;0<=b;b--)0===a[b].children.length?0<b?(d=a[b-1].children,d.splice(d.indexOf(a[b]),1)):this.clear():g(a[b],this.lb)},mj:function(a){var b=["return a"," - b",";"];this.Ve=new Function("a","b",b.join(a[0]));this.We=new Function("a","b",b.join(a[1]));this.lb=new Function("a","return {minX: a"+a[0]+", minY: a"+a[1]+", maxX: a"+a[2]+", maxY: a"+a[3]+"};")}}},{quickselect:1}]},{},[2])(2)});gl=b.ja})();function kl(a){this.a=gl(a);this.b={}}k=kl.prototype;k.Ca=function(a,b){var c={ca:a[0],fa:a[1],ea:a[2],ga:a[3],value:b};this.a.Ca(c);this.b[w(b)]=c};k.load=function(a,b){for(var c=Array(b.length),d=0,e=b.length;d<e;d++){var f=a[d],g=b[d],f={ca:f[0],fa:f[1],ea:f[2],ga:f[3],value:g};c[d]=f;this.b[w(g)]=f}this.a.load(c)};k.remove=function(a){a=w(a);var b=this.b[a];delete this.b[a];return null!==this.a.remove(b)}; +function ll(a,b,c){var d=w(c),d=a.b[d];$b([d.ca,d.fa,d.ea,d.ga],b)||(a.remove(c),a.Ca(b,c))}function ml(a){return a.a.all().map(function(a){return a.value})}function nl(a,b){return a.a.search({ca:b[0],fa:b[1],ea:b[2],ga:b[3]}).map(function(a){return a.value})}k.forEach=function(a,b){return pl(ml(this),a,b)};function ql(a,b,c,d){return pl(nl(a,b),c,d)}function pl(a,b,c){for(var d,e=0,f=a.length;e<f&&!(d=b.call(c,a[e]));e++);return d}k.Ya=function(){return Ha(this.b)}; +k.clear=function(){this.a.clear();this.b={}};k.H=function(){var a=this.a.data;return[a.ca,a.fa,a.ea,a.ga]};function P(a){a=a||{};jf.call(this,{attributions:a.attributions,logo:a.logo,projection:void 0,state:"ready",wrapX:void 0!==a.wrapX?a.wrapX:!0});this.S=na;this.qa=a.format;this.T=a.url;void 0!==a.loader?this.S=a.loader:void 0!==this.T&&(this.S=el(this.T,this.qa));this.qb=void 0!==a.strategy?a.strategy:fl;var b=void 0!==a.useSpatialIndex?a.useSpatialIndex:!0;this.a=b?new kl:null;this.Y=new kl;this.i={};this.o={};this.j={};this.s={};this.c=null;var c,d;a.features instanceof le?(c=a.features,d=c.a):Array.isArray(a.features)&& +(d=a.features);b||void 0!==c||(c=new le(d));void 0!==d&&rl(this,d);void 0!==c&&sl(this,c)}y(P,jf);k=P.prototype;k.rb=function(a){var b=w(a).toString();if(tl(this,b,a)){ul(this,b,a);var c=a.W();c?(b=c.H(),this.a&&this.a.Ca(b,a)):this.i[b]=a;this.b(new vl("addfeature",a))}this.u()};function ul(a,b,c){a.s[b]=[B(c,"change",a.Eh,a),B(c,"propertychange",a.Eh,a)]}function tl(a,b,c){var d=!0,e=c.Xa();void 0!==e?e.toString()in a.o?d=!1:a.o[e.toString()]=c:a.j[b]=c;return d}k.Jc=function(a){rl(this,a);this.u()}; +function rl(a,b){var c,d,e,f,g=[],h=[],l=[];d=0;for(e=b.length;d<e;d++)f=b[d],c=w(f).toString(),tl(a,c,f)&&h.push(f);d=0;for(e=h.length;d<e;d++){f=h[d];c=w(f).toString();ul(a,c,f);var m=f.W();m?(c=m.H(),g.push(c),l.push(f)):a.i[c]=f}a.a&&a.a.load(g,l);d=0;for(e=h.length;d<e;d++)a.b(new vl("addfeature",h[d]))} +function sl(a,b){var c=!1;B(a,"addfeature",function(a){c||(c=!0,b.push(a.feature),c=!1)});B(a,"removefeature",function(a){c||(c=!0,b.remove(a.feature),c=!1)});B(b,"add",function(a){c||(a=a.element,c=!0,this.rb(a),c=!1)},a);B(b,"remove",function(a){c||(a=a.element,c=!0,this.nb(a),c=!1)},a);a.c=b} +k.clear=function(a){if(a){for(var b in this.s)this.s[b].forEach(Ka);this.c||(this.s={},this.o={},this.j={})}else if(this.a){this.a.forEach(this.Sf,this);for(var c in this.i)this.Sf(this.i[c])}this.c&&this.c.clear();this.a&&this.a.clear();this.Y.clear();this.i={};this.b(new vl("clear"));this.u()};k.wg=function(a,b){if(this.a)return this.a.forEach(a,b);if(this.c)return this.c.forEach(a,b)};function wl(a,b,c){a.ub([b[0],b[1],b[0],b[1]],function(a){if(a.W().sg(b))return c.call(void 0,a)})} +k.ub=function(a,b,c){if(this.a)return ql(this.a,a,b,c);if(this.c)return this.c.forEach(b,c)};k.xg=function(a,b,c){return this.ub(a,function(d){if(d.W().Ka(a)&&(d=b.call(c,d)))return d})};k.Fg=function(){return this.c};k.oe=function(){var a;this.c?a=this.c.a:this.a&&(a=ml(this.a),Ha(this.i)||mb(a,Ga(this.i)));return a};k.Eg=function(a){var b=[];wl(this,a,function(a){b.push(a)});return b};k.ef=function(a){return nl(this.a,a)}; +k.Ag=function(a,b){var c=a[0],d=a[1],e=null,f=[NaN,NaN],g=Infinity,h=[-Infinity,-Infinity,Infinity,Infinity],l=b?b:qc;ql(this.a,h,function(a){if(l(a)){var b=a.W(),p=g;g=b.sb(c,d,f,g);g<p&&(e=a,a=Math.sqrt(g),h[0]=c-a,h[1]=d-a,h[2]=c+a,h[3]=d+a)}});return e};k.H=function(){return this.a.H()};k.Dg=function(a){a=this.o[a.toString()];return void 0!==a?a:null};k.Ch=function(){return this.qa};k.Dh=function(){return this.T}; +k.Eh=function(a){a=a.target;var b=w(a).toString(),c=a.W();c?(c=c.H(),b in this.i?(delete this.i[b],this.a&&this.a.Ca(c,a)):this.a&&ll(this.a,c,a)):b in this.i||(this.a&&this.a.remove(a),this.i[b]=a);c=a.Xa();void 0!==c?(c=c.toString(),b in this.j?(delete this.j[b],this.o[c]=a):this.o[c]!==a&&(xl(this,a),this.o[c]=a)):b in this.j||(xl(this,a),this.j[b]=a);this.u();this.b(new vl("changefeature",a))};k.Ya=function(){return this.a.Ya()&&Ha(this.i)}; +k.Pc=function(a,b,c){var d=this.Y;a=this.qb(a,b);var e,f;e=0;for(f=a.length;e<f;++e){var g=a[e];ql(d,g,function(a){return Ub(a.extent,g)})||(this.S.call(this,g,b,c),d.Ca(g,{extent:g.slice()}))}};k.nb=function(a){var b=w(a).toString();b in this.i?delete this.i[b]:this.a&&this.a.remove(a);this.Sf(a);this.u()};k.Sf=function(a){var b=w(a).toString();this.s[b].forEach(Ka);delete this.s[b];var c=a.Xa();void 0!==c?delete this.o[c.toString()]:delete this.j[b];this.b(new vl("removefeature",a))}; +function xl(a,b){for(var c in a.o)if(a.o[c]===b){delete a.o[c];break}}function vl(a,b){Wa.call(this,a);this.feature=b}y(vl,Wa);function yl(a){this.c=a.source;this.Aa=Xc();this.i=Oe();this.j=[0,0];this.v=null;Hk.call(this,{attributions:a.attributions,canvasFunction:this.Dj.bind(this),logo:a.logo,projection:a.projection,ratio:a.ratio,resolutions:a.resolutions,state:this.c.V()});this.S=null;this.s=void 0;this.xh(a.style);B(this.c,"change",this.en,this)}y(yl,Hk);k=yl.prototype; +k.Dj=function(a,b,c,d,e){var f=new dk(.5*b/c,a,b);this.c.Pc(a,b,e);var g=!1;this.c.ub(a,function(a){var d;if(!(d=g)){var e;(d=a.ec())?e=d.call(a,b):this.s&&(e=this.s(a,b));if(e){var n,p=!1;Array.isArray(e)||(e=[e]);d=0;for(n=e.length;d<n;++d)p=lk(f,a,e[d],jk(b,c),this.dn,this)||p;d=p}else d=!1}g=d},this);ek(f);if(g)return null;this.j[0]!=d[0]||this.j[1]!=d[1]?(this.i.canvas.width=d[0],this.i.canvas.height=d[1],this.j[0]=d[0],this.j[1]=d[1]):this.i.clearRect(0,0,d[0],d[1]);a=zl(this,kc(a),b,c,d);f.Pa(this.i, +c,a,0,{});this.v=f;return this.i.canvas};k.ra=function(a,b,c,d,e){if(this.v){var f={};return this.v.ra(a,b,0,d,function(a){var b=w(a).toString();if(!(b in f))return f[b]=!0,e(a)})}};k.an=function(){return this.c};k.bn=function(){return this.S};k.cn=function(){return this.s};function zl(a,b,c,d,e){return qh(a.Aa,e[0]/2,e[1]/2,d/c,-d/c,0,-b[0],-b[1])}k.dn=function(){this.u()};k.en=function(){lf(this,this.c.V())};k.xh=function(a){this.S=void 0!==a?a:vj;this.s=a?tj(this.S):void 0;this.u()};function Al(a){Jj.call(this,a);this.f=null;this.s=Xc();this.o=this.c=null}y(Al,Jj);Al.prototype.ra=function(a,b,c,d){var e=this.a;return e.ha().ra(a,b.viewState.resolution,b.viewState.rotation,b.skippedFeatureUids,function(a){return c.call(d,a,e)})}; +Al.prototype.Cc=function(a,b,c,d){if(this.f&&this.f.a())if(this.a.ha()instanceof yl){if(a=a.slice(),sh(b.pixelToCoordinateMatrix,a,a),this.ra(a,b,qc,this))return c.call(d,this.a)}else if(this.c||(this.c=Xc(),cd(this.s,this.c)),b=[0,0],sh(this.c,a,b),this.o||(this.o=Oe(1,1)),this.o.clearRect(0,0,1,1),this.o.drawImage(this.f?this.f.a():null,b[0],b[1],1,1,0,0,1,1),0<this.o.getImageData(0,0,1,1).data[3])return c.call(d,this.a)}; +Al.prototype.l=function(a,b){var c=a.pixelRatio,d=a.viewState,e=d.center,f=d.resolution,g=this.a.ha(),h=a.viewHints,l=a.extent;void 0!==b.extent&&(l=mc(l,b.extent));h[0]||h[1]||hc(l)||(d=g.A(l,f,c,d.projection))&&vh(this,d)&&(this.f=d);if(this.f){var d=this.f,h=d.H(),l=d.$(),m=d.f,f=c*l/(f*m);qh(this.s,c*a.size[0]/2,c*a.size[1]/2,f,f,0,m*(h[0]-e[0])/l,m*(e[1]-h[3])/l);this.c=null;xh(a.attributions,d.l);yh(a,g)}return!!this.f};function Bl(a){Jj.call(this,a);this.c=Oe();this.o=null;this.j=Lb();this.S=[0,0,0];this.D=Xc();this.C=0}y(Bl,Jj);Bl.prototype.i=function(a,b,c){var d=Mj(this,a,0);Kj(this,"precompose",c,a,d);Cl(this,c,a,b);Lj(this,c,a,d)}; +Bl.prototype.l=function(a,b){function c(a){a=a.V();return 2==a||4==a||3==a&&!r}var d=a.pixelRatio,e=a.viewState,f=e.projection,g=this.a,h=g.ha(),l=h.eb(f),m=l.Lb(e.resolution,this.C),n=l.$(m),p=e.center;n==e.resolution?(p=Ah(p,n,a.size),e=lc(p,n,e.rotation,a.size)):e=a.extent;void 0!==b.extent&&(e=mc(e,b.extent));if(hc(e))return!1;n=sf(l,e,n);p={};p[m]={};var q=this.Qd(h,f,p),r=g.c(),u=Lb(),x=new fe(0,0,0,0),v,D,A,z;for(A=n.ca;A<=n.ea;++A)for(z=n.fa;z<=n.ga;++z)v=h.ac(m,A,z,d,f),!c(v)&&v.a&&(v=v.a), +c(v)?p[m][v.ma.toString()]=v:(D=qf(l,v.ma,q,x,u),D||(v=rf(l,v.ma,x,u))&&q(m+1,v));q=Object.keys(p).map(Number);q.sort(ib);var u=[],F,x=0;for(A=q.length;x<A;++x)for(F in v=q[x],z=p[v],z)v=z[F],2==v.V()&&u.push(v);this.o=u;zh(a.usedTiles,h,m,n);Bh(a,h,l,d,f,e,m,g.f());wh(a,h);yh(a,h);return!0};Bl.prototype.Cc=function(a,b,c,d){var e=this.c.canvas,f=b.size;e.width=f[0];e.height=f[1];this.i(b,jh(this.a),this.c);if(0<this.c.getImageData(a[0],a[1],1,1).data[3])return c.call(d,this.a)}; +function Cl(a,b,c,d){var e=c.pixelRatio,f=c.viewState,g=f.center,h=f.projection,l=f.resolution,f=f.rotation,m=c.size,n=Math.round(e*m[0]/2),p=Math.round(e*m[1]/2),m=e/l,q=a.a,r=q.ha(),u=r.Ud(h),x=r.eb(h),q=ab(q,"render"),v=b,D,A,z,F;if(f||q)v=a.c,D=v.canvas,F=x.Lb(l),z=r.$d(F,e,h),F=hf(x.Ja(F)),z=z[0]/F[0],l=b.canvas.width*z,A=b.canvas.height*z,F=Math.round(Math.sqrt(l*l+A*A)),D.width!=F?D.width=D.height=F:v.clearRect(0,0,F,F),D=(F-l)/2/z,A=(F-A)/2/z,m*=z,n=Math.round(z*(n+D)),p=Math.round(z*(p+A)); +l=v.globalAlpha;v.globalAlpha=d.opacity;var N=a.o,K,X=r.jf(h)&&1==d.opacity;X||(N.reverse(),K=[]);var oa=d.extent;if(d=void 0!==oa){var H=fc(oa),ya=ec(oa),Ua=dc(oa),oa=cc(oa);sh(c.coordinateToPixelMatrix,H,H);sh(c.coordinateToPixelMatrix,ya,ya);sh(c.coordinateToPixelMatrix,Ua,Ua);sh(c.coordinateToPixelMatrix,oa,oa);var Xa=D||0,Va=A||0;v.save();var Aa=v.canvas.width*e/2,Qb=v.canvas.height*e/2;hj(v,-f,Aa,Qb);v.beginPath();v.moveTo(H[0]*e+Xa,H[1]*e+Va);v.lineTo(ya[0]*e+Xa,ya[1]*e+Va);v.lineTo(Ua[0]* +e+Xa,Ua[1]*e+Va);v.lineTo(oa[0]*e+Xa,oa[1]*e+Va);v.clip();hj(v,f,Aa,Qb)}H=0;for(ya=N.length;H<ya;++H){var Ua=N[H],oa=Ua.ma,Qb=x.Ea(oa,a.j),Aa=oa[0],Nb=cc(x.Ea(x.qd(g,Aa,a.S))),oa=Math.round(ic(Qb)*m),Xa=Math.round(jc(Qb)*m),Va=Math.round((Qb[0]-Nb[0])*m/oa)*oa+n+Math.round((Nb[0]-g[0])*m),Qb=Math.round((Nb[1]-Qb[3])*m/Xa)*Xa+p+Math.round((g[1]-Nb[1])*m);if(!X){Nb=[Va,Qb,Va+oa,Qb+Xa];v.save();for(var kk=0,vs=K.length;kk<vs;++kk){var De=K[kk];nc(Nb,De)&&(v.beginPath(),v.moveTo(Nb[0],Nb[1]),v.lineTo(Nb[0], +Nb[3]),v.lineTo(Nb[2],Nb[3]),v.lineTo(Nb[2],Nb[1]),v.moveTo(De[0],De[1]),v.lineTo(De[2],De[1]),v.lineTo(De[2],De[3]),v.lineTo(De[0],De[3]),v.closePath(),v.clip())}K.push(Nb)}Aa=r.$d(Aa,e,h);v.drawImage(Ua.$a(),u,u,Aa[0],Aa[1],Va,Qb,oa,Xa);X||v.restore()}d&&v.restore();q&&(e=D-n/z+n,h=A-p/z+p,g=qh(a.D,F/2-e,F/2-h,m,-m,-f,-g[0]+e/m,-g[1]-h/m),Kj(a,"render",v,c,g));(f||q)&&b.drawImage(v.canvas,-Math.round(D),-Math.round(A),F/z,F/z);v.globalAlpha=l};function Dl(a){Jj.call(this,a);this.c=!1;this.C=-1;this.A=NaN;this.v=Lb();this.o=this.U=null;this.j=Oe()}y(Dl,Jj); +Dl.prototype.i=function(a,b,c){var d=a.extent,e=a.pixelRatio,f=b.Qc?a.skippedFeatureUids:{},g=a.viewState,h=g.projection,g=g.rotation,l=h.H(),m=this.a.ha(),n=Mj(this,a,0);Kj(this,"precompose",c,a,n);var p=this.o;if(p&&!p.Ya()){var q;ab(this.a,"render")?(this.j.canvas.width=c.canvas.width,this.j.canvas.height=c.canvas.height,q=this.j):q=c;var r=q.globalAlpha;q.globalAlpha=b.opacity;b=a.size[0]*e;var u=a.size[1]*e;hj(q,-g,b/2,u/2);p.Pa(q,e,n,g,f);if(m.D&&h.a&&!Ub(l,d)){for(var h=d[0],m=ic(l),x=0;h< +l[0];)--x,n=m*x,n=Mj(this,a,n),p.Pa(q,e,n,g,f),h+=m;x=0;for(h=d[2];h>l[2];)++x,n=m*x,n=Mj(this,a,n),p.Pa(q,e,n,g,f),h-=m;n=Mj(this,a,0)}hj(q,g,b/2,u/2);q!=c&&(Kj(this,"render",q,a,n),c.drawImage(q.canvas,0,0));q.globalAlpha=r}Lj(this,c,a,n)};Dl.prototype.ra=function(a,b,c,d){if(this.o){var e=this.a,f={};return this.o.ra(a,b.viewState.resolution,b.viewState.rotation,{},function(a){var b=w(a).toString();if(!(b in f))return f[b]=!0,c.call(d,a,e)})}};Dl.prototype.D=function(){uh(this)}; +Dl.prototype.l=function(a){function b(a){var b,d=a.ec();d?b=d.call(a,m):(d=c.i)&&(b=d(a,m));if(b){if(b){d=!1;if(Array.isArray(b))for(var e=0,f=b.length;e<f;++e)d=lk(q,a,b[e],jk(m,n),this.D,this)||d;else d=lk(q,a,b,jk(m,n),this.D,this)||d;a=d}else a=!1;this.c=this.c||a}}var c=this.a,d=c.ha();xh(a.attributions,d.l);yh(a,d);var e=a.viewHints[0],f=a.viewHints[1],g=c.S,h=c.T;if(!this.c&&!g&&e||!h&&f)return!0;var l=a.extent,h=a.viewState,e=h.projection,m=h.resolution,n=a.pixelRatio,f=c.g,p=c.a,g=xj(c); +void 0===g&&(g=ik);l=Ob(l,p*m);p=h.projection.H();d.D&&h.projection.a&&!Ub(p,a.extent)&&(a=Math.max(ic(l)/2,ic(p)),l[0]=p[0]-a,l[2]=p[2]+a);if(!this.c&&this.A==m&&this.C==f&&this.U==g&&Ub(this.v,l))return!0;this.o=null;this.c=!1;var q=new dk(.5*m/n,l,m,c.a);d.Pc(l,m,e);if(g){var r=[];d.ub(l,function(a){r.push(a)},this);r.sort(g);r.forEach(b,this)}else d.ub(l,b,this);ek(q);this.A=m;this.C=f;this.U=g;this.v=l;this.o=q;return!0};function El(a,b){var c=/\{z\}/g,d=/\{x\}/g,e=/\{y\}/g,f=/\{-y\}/g;return function(g){if(g)return a.replace(c,g[0].toString()).replace(d,g[1].toString()).replace(e,function(){return(-g[2]-1).toString()}).replace(f,function(){var a=b.a?b.a[g[0]]:null;return(a.ga-a.fa+1+g[2]).toString()})}}function Fl(a,b){for(var c=a.length,d=Array(c),e=0;e<c;++e)d[e]=El(a[e],b);return Gl(d)}function Gl(a){return 1===a.length?a[0]:function(b,c,d){if(b)return a[xa((b[1]<<b[0])+b[2],a.length)](b,c,d)}} +function Hl(){}function Il(a){var b=[],c=/\{(\d)-(\d)\}/.exec(a)||/\{([a-z])-([a-z])\}/.exec(a);if(c){var d=c[2].charCodeAt(0),e;for(e=c[1].charCodeAt(0);e<=d;++e)b.push(a.replace(c[0],String.fromCharCode(e)))}else b.push(a);return b};function Jl(a){zf.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,extent:a.extent,logo:a.logo,opaque:a.opaque,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tilePixelRatio:a.tilePixelRatio,wrapX:a.wrapX});this.tileLoadFunction=a.tileLoadFunction;this.tileUrlFunction=this.vc?this.vc.bind(this):Hl;this.urls=null;a.urls?this.bb(a.urls):a.url&&this.Va(a.url);a.tileUrlFunction&&this.Qa(a.tileUrlFunction)}y(Jl,zf);k=Jl.prototype;k.fb=function(){return this.tileLoadFunction}; +k.gb=function(){return this.tileUrlFunction};k.hb=function(){return this.urls};k.Bh=function(a){a=a.target;switch(a.V()){case 1:this.b(new Df("tileloadstart",a));break;case 2:this.b(new Df("tileloadend",a));break;case 3:this.b(new Df("tileloaderror",a))}};k.kb=function(a){this.a.clear();this.tileLoadFunction=a;this.u()};k.Qa=function(a,b){this.tileUrlFunction=a;"undefined"!==typeof b?Bf(this,b):this.u()}; +k.Va=function(a){var b=this.urls=Il(a);this.Qa(this.vc?this.vc.bind(this):Fl(b,this.tileGrid),a)};k.bb=function(a){this.urls=a;var b=a.join("\n");this.Qa(this.vc?this.vc.bind(this):Fl(a,this.tileGrid),b)};k.Yf=function(a,b,c){a=this.Eb(a,b,c);Ze(this.a,a)&&this.a.get(a)};function Kl(a){Jl.call(this,{attributions:a.attributions,cacheSize:void 0!==a.cacheSize?a.cacheSize:128,extent:a.extent,logo:a.logo,opaque:!1,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction?a.tileLoadFunction:Ll,tileUrlFunction:a.tileUrlFunction,tilePixelRatio:a.tilePixelRatio,url:a.url,urls:a.urls,wrapX:void 0===a.wrapX?!0:a.wrapX});this.c=a.format?a.format:null;this.tileClass=a.tileClass?a.tileClass:Kk}y(Kl,Jl); +Kl.prototype.ac=function(a,b,c,d,e){var f=this.Eb(a,b,c);if(Ze(this.a,f))return this.a.get(f);a=[a,b,c];d=(b=Cf(this,a,e))?this.tileUrlFunction(b,d,e):void 0;d=new this.tileClass(a,void 0!==d?0:4,void 0!==d?d:"",this.c,this.tileLoadFunction);B(d,"change",this.Bh,this);this.a.set(f,d);return d};Kl.prototype.$d=function(a,b){var c=hf(this.tileGrid.Ja(a));return[c[0]*b,c[1]*b]};function Ll(a,b){a.ki(dl(b,a.l))};var Ml={image:Nj,hybrid:["Polygon","LineString"]},Nl={hybrid:["Image","Text"],vector:Nj};function Ol(a){Bl.call(this,a);this.U=!1;this.v=Xc();this.C="vector"==a.s?1:0}y(Ol,Bl); +Ol.prototype.i=function(a,b,c){var d=Mj(this,a,0);Kj(this,"precompose",c,a,d);var e=this.a.s;"vector"!==e&&Cl(this,c,a,b);if("image"!==e){var f=this.a,e=Nl[f.s],g=a.pixelRatio,h=b.Qc?a.skippedFeatureUids:{},l=a.viewState,m=l.center,n=l.rotation,p=a.size,l=g/l.resolution,q=f.ha(),r=q.bc(g),u=Mj(this,a,0);ab(f,"render")?(this.c.canvas.width=c.canvas.width,this.c.canvas.height=c.canvas.height,f=this.c):f=c;var x=f.globalAlpha;f.globalAlpha=b.opacity;b=this.o;var q=q.tileGrid,v,D,A,z,F,N,K,X;D=0;for(A= +b.length;D<A;++D)z=b[D],K=z.f,F=q.Ea(z.ma,this.j),v=z.ma[0],N="tile-pixels"==z.o.wb(),v=q.$(v),X=v/r,v=Math.round(g*p[0]/2),z=Math.round(g*p[1]/2),N?(F=fc(F),F=qh(this.v,v,z,l*X,l*X,n,(F[0]-m[0])/X,(m[1]-F[1])/X)):F=u,hj(f,-n,v,z),K.yd.Pa(f,g,F,n,h,e),hj(f,n,v,z);f!=c&&(Kj(this,"render",f,a,u),c.drawImage(f.canvas,0,0));f.globalAlpha=x}Lj(this,c,a,d)}; +function Pl(a,b,c){function d(a){var b,c=a.ec();c?b=c.call(a,u):(c=e.i)&&(b=c(a,u));if(b){Array.isArray(b)||(b=[b]);var c=D,d=v;if(b){var f=!1;if(Array.isArray(b))for(var g=0,h=b.length;g<h;++g)f=lk(d,a,b[g],c,this.A,this)||f;else f=lk(d,a,b,c,this.A,this)||f;a=f}else a=!1;this.U=this.U||a;l.gd=l.gd||a}}var e=a.a,f=c.pixelRatio;c=c.viewState.projection;var g=e.g,h=xj(e)||null,l=b.f;if(l.gd||l.bi!=g||l.Tf!=h){l.yd=null;l.gd=!1;var m=e.ha(),n=m.tileGrid,p=b.ma,q=b.o,r="tile-pixels"==q.wb(),u=n.$(p[0]), +x;r?(r=m=m.bc(f),n=hf(n.Ja(p[0])),n=[0,0,n[0]*r,n[1]*r]):(m=u,n=n.Ea(p),Oc(c,q)||(x=!0,b.vf(c)));l.gd=!1;var v=new dk(0,n,m,e.a),D=jk(m,f);b=b.c;h&&h!==l.Tf&&b.sort(h);n=0;for(p=b.length;n<p;++n)f=b[n],x&&f.W().jb(q,c),d.call(a,f);ek(v);l.bi=g;l.Tf=h;l.yd=v;l.resolution=NaN}} +Ol.prototype.ra=function(a,b,c,d){var e=b.pixelRatio,f=b.viewState.resolution;b=b.viewState.rotation;var g=this.a,h={},l=this.o,m=g.ha(),n=m.tileGrid,p,q,r,u,x,v;r=0;for(u=l.length;r<u;++r)v=l[r],q=v.ma,x=m.tileGrid.Ea(q,this.j),Sb(x,a)&&("tile-pixels"===v.o.wb()?(x=fc(x),f=m.bc(e),q=n.$(q[0])/f,q=[(a[0]-x[0])/q,(x[1]-a[1])/q]):q=a,v=v.f.yd,p=p||v.ra(q,f,b,{},function(a){var b=w(a).toString();if(!(b in h))return h[b]=!0,c.call(d,a,g)}));return p};Ol.prototype.A=function(){uh(this)}; +Ol.prototype.l=function(a,b){var c=Bl.prototype.l.call(this,a,b);if(c)for(var d=Object.keys(a.Ee||{}),e=0,f=this.o.length;e<f;++e){var g=this.o[e];Pl(this,g,a);var h=g,g=a,l=this.a,m=Ml[l.s];if(m){var n=g.pixelRatio,p=h.f,q=l.g;if(!pb(p.ui,d)||p.Uf!==q){p.ui=d;p.Uf=q;var q=h.g,r=l.ha(),u=r.tileGrid,x=h.ma[0],v=u.$(x),l=hf(u.Ja(x)),x=u.$(x),D=x/v,A=l[0]*n*D,z=l[1]*n*D;q.canvas.width=A/D+.5;q.canvas.height=z/D+.5;q.scale(1/D,1/D);q.translate(A/2,z/2);D="tile-pixels"==h.o.wb();v=n/v;r=r.bc(n);x/=r;h= +u.Ea(h.ma,this.j);D?h=qh(this.v,0,0,v*x,v*x,0,-l[0]*r/2,-l[1]*r/2):(h=kc(h),h=qh(this.v,0,0,v,-v,0,-h[0],-h[1]));p.yd.Pa(q,n,h,0,g.skippedFeatureUids||{},m)}}}return c};function Ql(a,b){Hh.call(this,0,b);this.f=Oe();this.b=this.f.canvas;this.b.style.width="100%";this.b.style.height="100%";this.b.className="ol-unselectable";a.insertBefore(this.b,a.childNodes[0]||null);this.a=!0;this.c=Xc()}y(Ql,Hh);Ql.prototype.Xe=function(a){return a instanceof cj?new Al(a):a instanceof dj?new Bl(a):a instanceof I?new Ol(a):a instanceof G?new Dl(a):null}; +function Rl(a,b,c){var d=a.i,e=a.f;if(ab(d,b)){var f=c.extent,g=c.pixelRatio,h=c.viewState.rotation,l=c.pixelRatio,m=c.viewState,n=m.resolution;a=qh(a.c,a.b.width/2,a.b.height/2,l/n,-l/n,-m.rotation,-m.center[0],-m.center[1]);f=new yj(e,g,f,a,h);d.b(new lh(b,d,f,c,e,null))}}Ql.prototype.X=function(){return"canvas"}; +Ql.prototype.Ce=function(a){if(a){var b=this.f,c=a.pixelRatio,d=Math.round(a.size[0]*c),c=Math.round(a.size[1]*c);this.b.width!=d||this.b.height!=c?(this.b.width=d,this.b.height=c):b.clearRect(0,0,d,c);var e=a.viewState.rotation;Ih(a);Rl(this,"precompose",a);var f=a.layerStatesArray;qb(f);hj(b,e,d/2,c/2);var g=a.viewState.resolution,h,l,m,n;h=0;for(l=f.length;h<l;++h)n=f[h],m=n.layer,m=Kh(this,m),nh(n,g)&&"ready"==n.R&&m.l(a,n)&&m.i(a,n,b);hj(b,-e,d/2,c/2);Rl(this,"postcompose",a);this.a||(this.b.style.display= +"",this.a=!0);Lh(this,a);a.postRenderFunctions.push(Jh)}else this.a&&(this.b.style.display="none",this.a=!1)};function Sl(a,b){th.call(this,a);this.target=b}y(Sl,th);Sl.prototype.Nd=na;Sl.prototype.th=na;function Tl(a){var b=document.createElement("DIV");b.style.position="absolute";Sl.call(this,a,b);this.f=null;this.c=Zc()}y(Tl,Sl);Tl.prototype.ra=function(a,b,c,d){var e=this.a;return e.ha().ra(a,b.viewState.resolution,b.viewState.rotation,b.skippedFeatureUids,function(a){return c.call(d,a,e)})};Tl.prototype.Nd=function(){Ve(this.target);this.f=null}; +Tl.prototype.yf=function(a,b){var c=a.viewState,d=c.center,e=c.resolution,f=c.rotation,g=this.f,h=this.a.ha(),l=a.viewHints,m=a.extent;void 0!==b.extent&&(m=mc(m,b.extent));l[0]||l[1]||hc(m)||(c=h.A(m,e,a.pixelRatio,c.projection))&&vh(this,c)&&(g=c);g&&(l=g.H(),m=g.$(),c=Xc(),qh(c,a.size[0]/2,a.size[1]/2,m/e,m/e,f,(l[0]-d[0])/m,(d[1]-l[3])/m),g!=this.f&&(d=g.a(this),d.style.maxWidth="none",d.style.position="absolute",Ve(this.target),this.target.appendChild(d),this.f=g),rh(c,this.c)||(Se(this.target, +c),$c(this.c,c)),xh(a.attributions,g.l),yh(a,h));return!0};function Ul(a){var b=document.createElement("DIV");b.style.position="absolute";Sl.call(this,a,b);this.c=!0;this.l=1;this.i=0;this.f={}}y(Ul,Sl);Ul.prototype.Nd=function(){Ve(this.target);this.i=0}; +Ul.prototype.yf=function(a,b){if(!b.visible)return this.c&&(this.target.style.display="none",this.c=!1),!0;var c=a.pixelRatio,d=a.viewState,e=d.projection,f=this.a,g=f.ha(),h=g.eb(e),l=g.Ud(e),m=h.Lb(d.resolution),n=h.$(m),p=d.center,q;n==d.resolution?(p=Ah(p,n,a.size),q=lc(p,n,d.rotation,a.size)):q=a.extent;void 0!==b.extent&&(q=mc(q,b.extent));var n=sf(h,q,n),r={};r[m]={};var u=this.Qd(g,e,r),x=f.c(),v=Lb(),D=new fe(0,0,0,0),A,z,F,N;for(F=n.ca;F<=n.ea;++F)for(N=n.fa;N<=n.ga;++N)A=g.ac(m,F,N,c,e), +z=A.V(),z=2==z||4==z||3==z&&!x,!z&&A.a&&(A=A.a),z=A.V(),2==z?r[m][A.ma.toString()]=A:4==z||3==z&&!x||(z=qf(h,A.ma,u,D,v),z||(A=rf(h,A.ma,D,v))&&u(m+1,A));var K;if(this.i!=g.g){for(K in this.f)x=this.f[+K],Ue(x.target);this.f={};this.i=g.g}v=Object.keys(r).map(Number);v.sort(ib);var u={},X;F=0;for(N=v.length;F<N;++F){K=v[F];K in this.f?x=this.f[K]:(x=h.qd(p,K),x=new Vl(h,x),u[K]=!0,this.f[K]=x);K=r[K];for(X in K){A=x;z=K[X];var oa=l,H=z.ma,ya=H[0],Ua=H[1],Xa=H[2],H=H.toString();if(!(H in A.a)){var ya= +hf(A.c.Ja(ya),A.o),Va=z.$a(A),Aa=Va.style;Aa.maxWidth="none";var Qb,Nb;0<oa?(Qb=document.createElement("DIV"),Nb=Qb.style,Nb.overflow="hidden",Nb.width=ya[0]+"px",Nb.height=ya[1]+"px",Aa.position="absolute",Aa.left=-oa+"px",Aa.top=-oa+"px",Aa.width=ya[0]+2*oa+"px",Aa.height=ya[1]+2*oa+"px",Qb.appendChild(Va)):(Aa.width=ya[0]+"px",Aa.height=ya[1]+"px",Qb=Va,Nb=Aa);Nb.position="absolute";Nb.left=(Ua-A.g[1])*ya[0]+"px";Nb.top=(A.g[2]-Xa)*ya[1]+"px";A.b||(A.b=document.createDocumentFragment());A.b.appendChild(Qb); +A.a[H]=z}}x.b&&(x.target.appendChild(x.b),x.b=null)}l=Object.keys(this.f).map(Number);l.sort(ib);F=Xc();X=0;for(v=l.length;X<v;++X)if(K=l[X],x=this.f[K],K in r)if(A=x.$(),N=x.Ia(),qh(F,a.size[0]/2,a.size[1]/2,A/d.resolution,A/d.resolution,d.rotation,(N[0]-p[0])/A,(p[1]-N[1])/A),x.setTransform(F),K in u){for(--K;0<=K;--K)if(K in this.f){this.f[K].target.parentNode&&this.f[K].target.parentNode.insertBefore(x.target,this.f[K].target.nextSibling);break}0>K&&this.target.insertBefore(x.target,this.target.childNodes[0]|| +null)}else{if(!a.viewHints[0]&&!a.viewHints[1]){z=pf(x.c,q,x.g[0],D);K=[];A=void 0;for(A in x.a)N=x.a[A],z.contains(N.ma)||K.push(N);z=0;for(oa=K.length;z<oa;++z)N=K[z],A=N.ma.toString(),Ue(N.$a(x)),delete x.a[A]}}else Ue(x.target),delete this.f[K];b.opacity!=this.l&&(this.l=this.target.style.opacity=b.opacity);b.visible&&!this.c&&(this.target.style.display="",this.c=!0);zh(a.usedTiles,g,m,n);Bh(a,g,h,c,e,q,m,f.f());wh(a,g);yh(a,g);return!0}; +function Vl(a,b){this.target=document.createElement("DIV");this.target.style.position="absolute";this.target.style.width="100%";this.target.style.height="100%";this.c=a;this.g=b;this.i=fc(a.Ea(b));this.l=a.$(b[0]);this.a={};this.b=null;this.f=Zc();this.o=[0,0]}Vl.prototype.Ia=function(){return this.i};Vl.prototype.$=function(){return this.l};Vl.prototype.setTransform=function(a){rh(a,this.f)||(Se(this.target,a),$c(this.f,a))};function Wl(a){this.i=Oe();var b=this.i.canvas;b.style.maxWidth="none";b.style.position="absolute";Sl.call(this,a,b);this.f=!1;this.l=-1;this.s=NaN;this.o=Lb();this.c=this.j=null;this.U=Xc();this.v=Xc()}y(Wl,Sl);k=Wl.prototype;k.Nd=function(){var a=this.i.canvas;a.width=a.width;this.l=0}; +k.th=function(a,b){var c=a.viewState,d=c.center,e=c.rotation,f=c.resolution,c=a.pixelRatio,g=a.size[0],h=a.size[1],l=g*c,m=h*c,d=qh(this.U,c*g/2,c*h/2,c/f,-c/f,-e,-d[0],-d[1]),f=this.i;f.canvas.width=l;f.canvas.height=m;g=qh(this.v,0,0,1/c,1/c,0,-(l-g)/2*c,-(m-h)/2*c);Se(f.canvas,g);Xl(this,"precompose",a,d);(g=this.c)&&!g.Ya()&&(f.globalAlpha=b.opacity,g.Pa(f,c,d,e,b.Qc?a.skippedFeatureUids:{}),Xl(this,"render",a,d));Xl(this,"postcompose",a,d)}; +function Xl(a,b,c,d){var e=a.i;a=a.a;ab(a,b)&&(d=new yj(e,c.pixelRatio,c.extent,d,c.viewState.rotation),a.b(new lh(b,a,d,c,e,null)))}k.ra=function(a,b,c,d){if(this.c){var e=this.a,f={};return this.c.ra(a,b.viewState.resolution,b.viewState.rotation,{},function(a){var b=w(a).toString();if(!(b in f))return f[b]=!0,c.call(d,a,e)})}};k.uh=function(){uh(this)}; +k.yf=function(a){function b(a){var b,d=a.ec();d?b=d.call(a,l):(d=c.i)&&(b=d(a,l));if(b){if(b){d=!1;if(Array.isArray(b))for(var e=0,f=b.length;e<f;++e)d=lk(n,a,b[e],jk(l,m),this.uh,this)||d;else d=lk(n,a,b,jk(l,m),this.uh,this)||d;a=d}else a=!1;this.f=this.f||a}}var c=this.a,d=c.ha();xh(a.attributions,d.l);yh(a,d);var e=a.viewHints[0],f=a.viewHints[1],g=c.S,h=c.T;if(!this.f&&!g&&e||!h&&f)return!0;var f=a.extent,g=a.viewState,e=g.projection,l=g.resolution,m=a.pixelRatio;a=c.g;h=c.a;g=xj(c);void 0=== +g&&(g=ik);f=Ob(f,h*l);if(!this.f&&this.s==l&&this.l==a&&this.j==g&&Ub(this.o,f))return!0;this.c=null;this.f=!1;var n=new dk(.5*l/m,f,l,c.a);d.Pc(f,l,e);if(g){var p=[];d.ub(f,function(a){p.push(a)},this);p.sort(g);p.forEach(b,this)}else d.ub(f,b,this);ek(n);this.s=l;this.l=a;this.j=g;this.o=f;this.c=n;return!0};function Yl(a,b){Hh.call(this,0,b);this.f=Oe();var c=this.f.canvas;c.style.position="absolute";c.style.width="100%";c.style.height="100%";c.className="ol-unselectable";a.insertBefore(c,a.childNodes[0]||null);this.c=Xc();this.b=document.createElement("DIV");this.b.className="ol-unselectable";c=this.b.style;c.position="absolute";c.width="100%";c.height="100%";B(this.b,"touchstart",Za);a.insertBefore(this.b,a.childNodes[0]||null);this.a=!0}y(Yl,Hh);Yl.prototype.ka=function(){Ue(this.b);Hh.prototype.ka.call(this)}; +Yl.prototype.Xe=function(a){if(a instanceof cj)a=new Tl(a);else if(a instanceof dj)a=new Ul(a);else if(a instanceof G)a=new Wl(a);else return null;return a};function Zl(a,b,c){var d=a.i;if(ab(d,b)){var e=c.extent,f=c.pixelRatio,g=c.viewState,h=g.rotation,l=a.f,m=l.canvas;qh(a.c,m.width/2,m.height/2,f/g.resolution,-f/g.resolution,-g.rotation,-g.center[0],-g.center[1]);a=new yj(l,f,e,a.c,h);d.b(new lh(b,d,a,c,l,null))}}Yl.prototype.X=function(){return"dom"}; +Yl.prototype.Ce=function(a){if(a){var b=this.i;if(ab(b,"precompose")||ab(b,"postcompose")){var b=this.f.canvas,c=a.pixelRatio;b.width=a.size[0]*c;b.height=a.size[1]*c}Zl(this,"precompose",a);b=a.layerStatesArray;qb(b);var c=a.viewState.resolution,d,e,f,g;d=0;for(e=b.length;d<e;++d)g=b[d],f=g.layer,f=Kh(this,f),this.b.insertBefore(f.target,this.b.childNodes[d]||null),nh(g,c)&&"ready"==g.R?f.yf(a,g)&&f.th(a,g):f.Nd();var b=a.layerStates,h;for(h in this.g)h in b||(f=this.g[h],Ue(f.target));this.a||(this.b.style.display= +"",this.a=!0);Ih(a);Lh(this,a);a.postRenderFunctions.push(Jh);Zl(this,"postcompose",a)}else this.a&&(this.b.style.display="none",this.a=!1)};function $l(a){this.b=a}function am(a){this.b=a}y(am,$l);am.prototype.X=function(){return 35632};function bm(a){this.b=a}y(bm,$l);bm.prototype.X=function(){return 35633};function cm(){this.b="precision mediump float;varying vec2 a;varying float b;uniform float k;uniform sampler2D l;void main(void){vec4 texColor=texture2D(l,a);gl_FragColor.rgb=texColor.rgb;float alpha=texColor.a*b*k;if(alpha==0.0){discard;}gl_FragColor.a=alpha;}"}y(cm,am);ba(cm); +function dm(){this.b="varying vec2 a;varying float b;attribute vec2 c;attribute vec2 d;attribute vec2 e;attribute float f;attribute float g;uniform mat4 h;uniform mat4 i;uniform mat4 j;void main(void){mat4 offsetMatrix=i;if(g==1.0){offsetMatrix=i*j;}vec4 offsets=offsetMatrix*vec4(e,0.,0.);gl_Position=h*vec4(c,0.,1.)+offsets;a=d;b=f;}"}y(dm,bm);ba(dm); +function em(a,b){this.o=a.getUniformLocation(b,"j");this.j=a.getUniformLocation(b,"i");this.i=a.getUniformLocation(b,"k");this.l=a.getUniformLocation(b,"h");this.b=a.getAttribLocation(b,"e");this.a=a.getAttribLocation(b,"f");this.f=a.getAttribLocation(b,"c");this.g=a.getAttribLocation(b,"g");this.c=a.getAttribLocation(b,"d")};function fm(a){this.b=void 0!==a?a:[]};function gm(a,b){this.l=a;this.b=b;this.a={};this.c={};this.f={};this.j=this.s=this.i=this.o=null;(this.g=jb(ma,"OES_element_index_uint"))&&b.getExtension("OES_element_index_uint");B(this.l,"webglcontextlost",this.ao,this);B(this.l,"webglcontextrestored",this.bo,this)}y(gm,Sa); +function hm(a,b,c){var d=a.b,e=c.b,f=String(w(c));if(f in a.a)d.bindBuffer(b,a.a[f].buffer);else{var g=d.createBuffer();d.bindBuffer(b,g);var h;34962==b?h=new Float32Array(e):34963==b&&(h=a.g?new Uint32Array(e):new Uint16Array(e));d.bufferData(b,h,35044);a.a[f]={Cb:c,buffer:g}}}function im(a,b){var c=a.b,d=String(w(b)),e=a.a[d];c.isContextLost()||c.deleteBuffer(e.buffer);delete a.a[d]}k=gm.prototype; +k.ka=function(){Ra(this.l);var a=this.b;if(!a.isContextLost()){for(var b in this.a)a.deleteBuffer(this.a[b].buffer);for(b in this.f)a.deleteProgram(this.f[b]);for(b in this.c)a.deleteShader(this.c[b]);a.deleteFramebuffer(this.i);a.deleteRenderbuffer(this.j);a.deleteTexture(this.s)}};k.$n=function(){return this.b}; +function jm(a){if(!a.i){var b=a.b,c=b.createFramebuffer();b.bindFramebuffer(b.FRAMEBUFFER,c);var d=km(b,1,1),e=b.createRenderbuffer();b.bindRenderbuffer(b.RENDERBUFFER,e);b.renderbufferStorage(b.RENDERBUFFER,b.DEPTH_COMPONENT16,1,1);b.framebufferTexture2D(b.FRAMEBUFFER,b.COLOR_ATTACHMENT0,b.TEXTURE_2D,d,0);b.framebufferRenderbuffer(b.FRAMEBUFFER,b.DEPTH_ATTACHMENT,b.RENDERBUFFER,e);b.bindTexture(b.TEXTURE_2D,null);b.bindRenderbuffer(b.RENDERBUFFER,null);b.bindFramebuffer(b.FRAMEBUFFER,null);a.i=c; +a.s=d;a.j=e}return a.i}function lm(a,b){var c=String(w(b));if(c in a.c)return a.c[c];var d=a.b,e=d.createShader(b.X());d.shaderSource(e,b.b);d.compileShader(e);return a.c[c]=e}function mm(a,b,c){var d=w(b)+"/"+w(c);if(d in a.f)return a.f[d];var e=a.b,f=e.createProgram();e.attachShader(f,lm(a,b));e.attachShader(f,lm(a,c));e.linkProgram(f);return a.f[d]=f}k.ao=function(){Fa(this.a);Fa(this.c);Fa(this.f);this.j=this.s=this.i=this.o=null};k.bo=function(){}; +k.we=function(a){if(a==this.o)return!1;this.b.useProgram(a);this.o=a;return!0};function nm(a,b,c){var d=a.createTexture();a.bindTexture(a.TEXTURE_2D,d);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MAG_FILTER,a.LINEAR);a.texParameteri(a.TEXTURE_2D,a.TEXTURE_MIN_FILTER,a.LINEAR);void 0!==b&&a.texParameteri(3553,10242,b);void 0!==c&&a.texParameteri(3553,10243,c);return d}function km(a,b,c){var d=nm(a,void 0,void 0);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,b,c,0,a.RGBA,a.UNSIGNED_BYTE,null);return d} +function om(a,b){var c=nm(a,33071,33071);a.texImage2D(a.TEXTURE_2D,0,a.RGBA,a.RGBA,a.UNSIGNED_BYTE,b);return c};function pm(a,b){this.C=this.A=void 0;this.j=kc(b);this.U=[];this.i=[];this.R=void 0;this.c=[];this.f=[];this.Ba=this.ya=void 0;this.a=[];this.D=this.o=null;this.S=void 0;this.ta=Zc();this.Aa=Zc();this.Y=this.T=void 0;this.Sa=Zc();this.za=this.ia=this.Ra=void 0;this.Gb=[];this.l=[];this.b=[];this.v=null;this.g=[];this.s=[];this.qa=void 0}y(pm,kh); +function qm(a,b){var c=a.v,d=a.o,e=a.Gb,f=a.l,g=b.b;return function(){if(!g.isContextLost()){var a,l;a=0;for(l=e.length;a<l;++a)g.deleteTexture(e[a]);a=0;for(l=f.length;a<l;++a)g.deleteTexture(f[a])}im(b,c);im(b,d)}} +function rm(a,b,c,d){var e=a.A,f=a.C,g=a.R,h=a.ya,l=a.Ba,m=a.S,n=a.T,p=a.Y,q=a.Ra?1:0,r=a.ia,u=a.za,x=a.qa,v=Math.cos(r),r=Math.sin(r),D=a.a.length,A=a.b.length,z,F,N,K,X,oa;for(z=0;z<c;z+=d)X=b[z]-a.j[0],oa=b[z+1]-a.j[1],F=A/8,N=-u*e,K=-u*(g-f),a.b[A++]=X,a.b[A++]=oa,a.b[A++]=N*v-K*r,a.b[A++]=N*r+K*v,a.b[A++]=n/l,a.b[A++]=(p+g)/h,a.b[A++]=m,a.b[A++]=q,N=u*(x-e),K=-u*(g-f),a.b[A++]=X,a.b[A++]=oa,a.b[A++]=N*v-K*r,a.b[A++]=N*r+K*v,a.b[A++]=(n+x)/l,a.b[A++]=(p+g)/h,a.b[A++]=m,a.b[A++]=q,N=u*(x-e),K= +u*f,a.b[A++]=X,a.b[A++]=oa,a.b[A++]=N*v-K*r,a.b[A++]=N*r+K*v,a.b[A++]=(n+x)/l,a.b[A++]=p/h,a.b[A++]=m,a.b[A++]=q,N=-u*e,K=u*f,a.b[A++]=X,a.b[A++]=oa,a.b[A++]=N*v-K*r,a.b[A++]=N*r+K*v,a.b[A++]=n/l,a.b[A++]=p/h,a.b[A++]=m,a.b[A++]=q,a.a[D++]=F,a.a[D++]=F+1,a.a[D++]=F+2,a.a[D++]=F,a.a[D++]=F+2,a.a[D++]=F+3}pm.prototype.tc=function(a,b){this.g.push(this.a.length);this.s.push(b);var c=a.la();rm(this,c,c.length,a.va())}; +pm.prototype.uc=function(a,b){this.g.push(this.a.length);this.s.push(b);var c=a.la();rm(this,c,c.length,a.va())};function sm(a,b){var c=b.b;a.U.push(a.a.length);a.i.push(a.a.length);a.v=new fm(a.b);hm(b,34962,a.v);a.o=new fm(a.a);hm(b,34963,a.o);var d={};tm(a.Gb,a.c,d,c);tm(a.l,a.f,d,c);a.A=void 0;a.C=void 0;a.R=void 0;a.c=null;a.f=null;a.ya=void 0;a.Ba=void 0;a.a=null;a.S=void 0;a.T=void 0;a.Y=void 0;a.Ra=void 0;a.ia=void 0;a.za=void 0;a.b=null;a.qa=void 0} +function tm(a,b,c,d){var e,f,g,h=b.length;for(g=0;g<h;++g)e=b[g],f=w(e).toString(),f in c?e=c[f]:(e=om(d,e),c[f]=e),a[g]=e} +pm.prototype.Pa=function(a,b,c,d,e,f,g,h,l,m,n){f=a.b;hm(a,34962,this.v);hm(a,34963,this.o);var p=cm.Zb(),q=dm.Zb(),q=mm(a,p,q);this.D?p=this.D:this.D=p=new em(f,q);a.we(q);f.enableVertexAttribArray(p.f);f.vertexAttribPointer(p.f,2,5126,!1,32,0);f.enableVertexAttribArray(p.b);f.vertexAttribPointer(p.b,2,5126,!1,32,8);f.enableVertexAttribArray(p.c);f.vertexAttribPointer(p.c,2,5126,!1,32,16);f.enableVertexAttribArray(p.a);f.vertexAttribPointer(p.a,1,5126,!1,32,24);f.enableVertexAttribArray(p.g);f.vertexAttribPointer(p.g, +1,5126,!1,32,28);q=this.Sa;qh(q,0,0,2/(c*e[0]),2/(c*e[1]),-d,-(b[0]-this.j[0]),-(b[1]-this.j[1]));b=this.Aa;c=2/e[0];e=2/e[1];ad(b);b[0]=c;b[5]=e;b[10]=1;b[15]=1;e=this.ta;ad(e);0!==d&&fd(e,-d);f.uniformMatrix4fv(p.l,!1,q);f.uniformMatrix4fv(p.j,!1,b);f.uniformMatrix4fv(p.o,!1,e);f.uniform1f(p.i,g);var r;if(void 0===l)um(this,f,a,h,this.Gb,this.U);else{if(m)a:{d=a.g?5125:5123;a=a.g?4:2;e=this.g.length-1;for(g=this.l.length-1;0<=g;--g)for(f.bindTexture(3553,this.l[g]),m=0<g?this.i[g-1]:0,b=this.i[g];0<= +e&&this.g[e]>=m;){r=this.g[e];c=this.s[e];q=w(c).toString();if(void 0===h[q]&&c.W()&&(void 0===n||nc(n,c.W().H()))&&(f.clear(f.COLOR_BUFFER_BIT|f.DEPTH_BUFFER_BIT),f.drawElements(4,b-r,d,r*a),b=l(c))){h=b;break a}b=r;e--}h=void 0}else f.clear(f.COLOR_BUFFER_BIT|f.DEPTH_BUFFER_BIT),um(this,f,a,h,this.l,this.i),h=(h=l(null))?h:void 0;r=h}f.disableVertexAttribArray(p.f);f.disableVertexAttribArray(p.b);f.disableVertexAttribArray(p.c);f.disableVertexAttribArray(p.a);f.disableVertexAttribArray(p.g);return r}; +function um(a,b,c,d,e,f){var g=c.g?5125:5123;c=c.g?4:2;if(Ha(d)){var h;a=0;d=e.length;for(h=0;a<d;++a){b.bindTexture(3553,e[a]);var l=f[a];b.drawElements(4,l-h,g,h*c);h=l}}else{h=0;var m,l=0;for(m=e.length;l<m;++l){b.bindTexture(3553,e[l]);for(var n=0<l?f[l-1]:0,p=f[l],q=n;h<a.g.length&&a.g[h]<=p;){var r=w(a.s[h]).toString();void 0!==d[r]?(q!==n&&b.drawElements(4,n-q,g,q*c),n=q=h===a.g.length-1?p:a.g[h+1]):n=h===a.g.length-1?p:a.g[h+1];h++}q!==n&&b.drawElements(4,n-q,g,q*c)}}} +pm.prototype.Tb=function(a){var b=a.Yb(),c=a.jc(1),d=a.ld(),e=a.pe(1),f=a.v,g=a.Ia(),h=a.U,l=a.j,m=a.Fb();a=a.i;var n;0===this.c.length?this.c.push(c):(n=this.c[this.c.length-1],w(n)!=w(c)&&(this.U.push(this.a.length),this.c.push(c)));0===this.f.length?this.f.push(e):(n=this.f[this.f.length-1],w(n)!=w(e)&&(this.i.push(this.a.length),this.f.push(e)));this.A=b[0];this.C=b[1];this.R=m[1];this.ya=d[1];this.Ba=d[0];this.S=f;this.T=g[0];this.Y=g[1];this.ia=l;this.Ra=h;this.za=a;this.qa=m[0]}; +function vm(a,b,c){this.f=b;this.c=a;this.g=c;this.a={}}function wm(a,b){var c=[],d;for(d in a.a)c.push(qm(a.a[d],b));return function(){for(var a=c.length,b,d=0;d<a;d++)b=c[d].apply(this,arguments);return b}}function xm(a,b){for(var c in a.a)sm(a.a[c],b)}vm.prototype.b=function(a,b){var c=this.a[b];void 0===c&&(c=new ym[b](this.c,this.f),this.a[b]=c);return c};vm.prototype.Ya=function(){return Ha(this.a)}; +vm.prototype.Pa=function(a,b,c,d,e,f,g,h){var l,m,n;l=0;for(m=Nj.length;l<m;++l)n=this.a[Nj[l]],void 0!==n&&n.Pa(a,b,c,d,e,f,g,h,void 0,!1)};function zm(a,b,c,d,e,f,g,h,l,m,n){var p=Am,q,r;for(q=Nj.length-1;0<=q;--q)if(r=a.a[Nj[q]],void 0!==r&&(r=r.Pa(b,c,d,e,p,f,g,h,l,m,n)))return r} +vm.prototype.ra=function(a,b,c,d,e,f,g,h,l,m){var n=b.b;n.bindFramebuffer(n.FRAMEBUFFER,jm(b));var p;void 0!==this.g&&(p=Ob(Xb(a),d*this.g));return zm(this,b,a,d,e,g,h,l,function(a){var b=new Uint8Array(4);n.readPixels(0,0,1,1,n.RGBA,n.UNSIGNED_BYTE,b);if(0<b[3]&&(a=m(a)))return a},!0,p)}; +function Bm(a,b,c,d,e,f,g,h){var l=c.b;l.bindFramebuffer(l.FRAMEBUFFER,jm(c));return void 0!==zm(a,c,b,d,e,f,g,h,function(){var a=new Uint8Array(4);l.readPixels(0,0,1,1,l.RGBA,l.UNSIGNED_BYTE,a);return 0<a[3]},!1)}var ym={Image:pm},Am=[1,1];function Cm(a,b,c,d,e,f,g){this.b=a;this.f=b;this.g=f;this.c=g;this.o=e;this.l=d;this.i=c;this.a=null}y(Cm,kh);k=Cm.prototype;k.sd=function(a){this.Tb(a.a)};k.sc=function(a){switch(a.X()){case "Point":this.uc(a,null);break;case "MultiPoint":this.tc(a,null);break;case "GeometryCollection":this.Ze(a,null)}};k.Ye=function(a,b){var c=(0,b.g)(a);c&&nc(this.g,c.H())&&(this.sd(b),this.sc(c))};k.Ze=function(a){a=a.c;var b,c;b=0;for(c=a.length;b<c;++b)this.sc(a[b])}; +k.uc=function(a,b){var c=this.b,d=(new vm(1,this.g)).b(0,"Image");d.Tb(this.a);d.uc(a,b);sm(d,c);d.Pa(this.b,this.f,this.i,this.l,this.o,this.c,1,{},void 0,!1);qm(d,c)()};k.tc=function(a,b){var c=this.b,d=(new vm(1,this.g)).b(0,"Image");d.Tb(this.a);d.tc(a,b);sm(d,c);d.Pa(this.b,this.f,this.i,this.l,this.o,this.c,1,{},void 0,!1);qm(d,c)()};k.Tb=function(a){this.a=a};function Dm(){this.b="precision mediump float;varying vec2 a;uniform float f;uniform sampler2D g;void main(void){vec4 texColor=texture2D(g,a);gl_FragColor.rgb=texColor.rgb;gl_FragColor.a=texColor.a*f;}"}y(Dm,am);ba(Dm);function Em(){this.b="varying vec2 a;attribute vec2 b;attribute vec2 c;uniform mat4 d;uniform mat4 e;void main(void){gl_Position=e*vec4(b,0.,1.);a=(d*vec4(c,0.,1.)).st;}"}y(Em,bm);ba(Em); +function Fm(a,b){this.g=a.getUniformLocation(b,"f");this.f=a.getUniformLocation(b,"e");this.i=a.getUniformLocation(b,"d");this.c=a.getUniformLocation(b,"g");this.b=a.getAttribLocation(b,"b");this.a=a.getAttribLocation(b,"c")};function Gm(a,b){th.call(this,b);this.f=a;this.S=new fm([-1,-1,0,0,1,-1,1,0,-1,1,0,1,1,1,1,1]);this.i=this.pb=null;this.l=void 0;this.s=Xc();this.U=Zc();this.v=null}y(Gm,th); +function Hm(a,b,c){var d=a.f.f;if(void 0===a.l||a.l!=c){b.postRenderFunctions.push(function(a,b,c){a.isContextLost()||(a.deleteFramebuffer(b),a.deleteTexture(c))}.bind(null,d,a.i,a.pb));b=km(d,c,c);var e=d.createFramebuffer();d.bindFramebuffer(36160,e);d.framebufferTexture2D(36160,36064,3553,b,0);a.pb=b;a.i=e;a.l=c}else d.bindFramebuffer(36160,a.i)} +Gm.prototype.vh=function(a,b,c){Im(this,"precompose",c,a);hm(c,34962,this.S);var d=c.b,e=Dm.Zb(),f=Em.Zb(),e=mm(c,e,f);this.v?f=this.v:this.v=f=new Fm(d,e);c.we(e)&&(d.enableVertexAttribArray(f.b),d.vertexAttribPointer(f.b,2,5126,!1,16,0),d.enableVertexAttribArray(f.a),d.vertexAttribPointer(f.a,2,5126,!1,16,8),d.uniform1i(f.c,0));d.uniformMatrix4fv(f.i,!1,this.s);d.uniformMatrix4fv(f.f,!1,this.U);d.uniform1f(f.g,b.opacity);d.bindTexture(3553,this.pb);d.drawArrays(5,0,4);Im(this,"postcompose",c,a)}; +function Im(a,b,c,d){a=a.a;if(ab(a,b)){var e=d.viewState;a.b(new lh(b,a,new Cm(c,e.center,e.resolution,e.rotation,d.size,d.extent,d.pixelRatio),d,null,c))}}Gm.prototype.zf=function(){this.i=this.pb=null;this.l=void 0};function Jm(a,b){Gm.call(this,a,b);this.j=this.o=this.c=null}y(Jm,Gm);function Km(a,b){var c=b.a();return om(a.f.f,c)}Jm.prototype.ra=function(a,b,c,d){var e=this.a;return e.ha().ra(a,b.viewState.resolution,b.viewState.rotation,b.skippedFeatureUids,function(a){return c.call(d,a,e)})}; +Jm.prototype.Af=function(a,b){var c=this.f.f,d=a.pixelRatio,e=a.viewState,f=e.center,g=e.resolution,h=e.rotation,l=this.c,m=this.pb,n=this.a.ha(),p=a.viewHints,q=a.extent;void 0!==b.extent&&(q=mc(q,b.extent));p[0]||p[1]||hc(q)||(e=n.A(q,g,d,e.projection))&&vh(this,e)&&(l=e,m=Km(this,e),this.pb&&a.postRenderFunctions.push(function(a,b){a.isContextLost()||a.deleteTexture(b)}.bind(null,c,this.pb)));l&&(c=this.f.c.l,Lm(this,c.width,c.height,d,f,g,h,l.H()),this.j=null,d=this.s,ad(d),ed(d,1,-1),dd(d,0, +-1),this.c=l,this.pb=m,xh(a.attributions,l.l),yh(a,n));return!0};function Lm(a,b,c,d,e,f,g,h){b*=f;c*=f;a=a.U;ad(a);ed(a,2*d/b,2*d/c);fd(a,-g);dd(a,h[0]-e[0],h[1]-e[1]);ed(a,(h[2]-h[0])/2,(h[3]-h[1])/2);dd(a,1,1)}Jm.prototype.le=function(a,b){return void 0!==this.ra(a,b,qc,this)}; +Jm.prototype.Cc=function(a,b,c,d){if(this.c&&this.c.a())if(this.a.ha()instanceof yl){if(a=a.slice(),sh(b.pixelToCoordinateMatrix,a,a),this.ra(a,b,qc,this))return c.call(d,this.a)}else{var e=[this.c.a().width,this.c.a().height];if(!this.j){var f=b.size;b=Xc();ad(b);dd(b,-1,-1);ed(b,2/f[0],2/f[1]);dd(b,0,f[1]);ed(b,1,-1);f=Xc();cd(this.U,f);var g=Xc();ad(g);dd(g,0,e[1]);ed(g,1,-1);ed(g,e[0]/2,e[1]/2);dd(g,1,1);var h=Xc();bd(g,f,h);bd(h,b,h);this.j=h}b=[0,0];sh(this.j,a,b);if(!(0>b[0]||b[0]>e[0]||0> +b[1]||b[1]>e[1])&&(this.o||(this.o=Oe(1,1)),this.o.clearRect(0,0,1,1),this.o.drawImage(this.c.a(),b[0],b[1],1,1,0,0,1,1),0<this.o.getImageData(0,0,1,1).data[3]))return c.call(d,this.a)}};function Mm(){this.b="precision mediump float;varying vec2 a;uniform sampler2D e;void main(void){gl_FragColor=texture2D(e,a);}"}y(Mm,am);ba(Mm);function Nm(){this.b="varying vec2 a;attribute vec2 b;attribute vec2 c;uniform vec4 d;void main(void){gl_Position=vec4(b*d.xy+d.zw,0.,1.);a=c;}"}y(Nm,bm);ba(Nm);function Om(a,b){this.g=a.getUniformLocation(b,"e");this.f=a.getUniformLocation(b,"d");this.b=a.getAttribLocation(b,"b");this.a=a.getAttribLocation(b,"c")};function Pm(a,b){Gm.call(this,a,b);this.D=Mm.Zb();this.T=Nm.Zb();this.c=null;this.C=new fm([0,0,0,1,1,0,1,1,0,1,0,0,1,1,1,0]);this.A=this.o=null;this.j=-1;this.R=[0,0]}y(Pm,Gm);k=Pm.prototype;k.ka=function(){im(this.f.c,this.C);Gm.prototype.ka.call(this)};k.Qd=function(a,b,c){var d=this.f;return function(e,f){return Af(a,b,e,f,function(a){var b=Ze(d.a,a.ib());b&&(c[e]||(c[e]={}),c[e][a.ma.toString()]=a);return b})}};k.zf=function(){Gm.prototype.zf.call(this);this.c=null}; +k.Af=function(a,b,c){var d=this.f,e=c.b,f=a.viewState,g=f.projection,h=this.a,l=h.ha(),m=l.eb(g),n=m.Lb(f.resolution),p=m.$(n),q=l.$d(n,a.pixelRatio,g),r=q[0]/hf(m.Ja(n),this.R)[0],u=p/r,x=l.Ud(g),v=f.center,D;p==f.resolution?(v=Ah(v,p,a.size),D=lc(v,p,f.rotation,a.size)):D=a.extent;p=sf(m,D,p);if(this.o&&he(this.o,p)&&this.j==l.g)u=this.A;else{var A=[p.ea-p.ca+1,p.ga-p.fa+1],z=Math.pow(2,Math.ceil(Math.log(Math.max(A[0]*q[0],A[1]*q[1]))/Math.LN2)),A=u*z,F=m.Ia(n),N=F[0]+p.ca*q[0]*u,u=F[1]+p.fa*q[1]* +u,u=[N,u,N+A,u+A];Hm(this,a,z);e.viewport(0,0,z,z);e.clearColor(0,0,0,0);e.clear(16384);e.disable(3042);z=mm(c,this.D,this.T);c.we(z);this.c||(this.c=new Om(e,z));hm(c,34962,this.C);e.enableVertexAttribArray(this.c.b);e.vertexAttribPointer(this.c.b,2,5126,!1,16,0);e.enableVertexAttribArray(this.c.a);e.vertexAttribPointer(this.c.a,2,5126,!1,16,8);e.uniform1i(this.c.g,0);c={};c[n]={};var K=this.Qd(l,g,c),X=h.c(),z=!0,N=Lb(),oa=new fe(0,0,0,0),H,ya,Ua;for(ya=p.ca;ya<=p.ea;++ya)for(Ua=p.fa;Ua<=p.ga;++Ua){F= +l.ac(n,ya,Ua,r,g);if(void 0!==b.extent&&(H=m.Ea(F.ma,N),!nc(H,b.extent)))continue;H=F.V();H=2==H||4==H||3==H&&!X;!H&&F.a&&(F=F.a);H=F.V();if(2==H){if(Ze(d.a,F.ib())){c[n][F.ma.toString()]=F;continue}}else if(4==H||3==H&&!X)continue;z=!1;H=qf(m,F.ma,K,oa,N);H||(F=rf(m,F.ma,oa,N))&&K(n+1,F)}b=Object.keys(c).map(Number);b.sort(ib);for(var K=new Float32Array(4),Xa,Va,Aa,X=0,oa=b.length;X<oa;++X)for(Xa in Va=c[b[X]],Va)F=Va[Xa],H=m.Ea(F.ma,N),ya=2*(H[2]-H[0])/A,Ua=2*(H[3]-H[1])/A,Aa=2*(H[0]-u[0])/A-1, +H=2*(H[1]-u[1])/A-1,Wc(K,ya,Ua,Aa,H),e.uniform4fv(this.c.f,K),Qm(d,F,q,x*r),e.drawArrays(5,0,4);z?(this.o=p,this.A=u,this.j=l.g):(this.A=this.o=null,this.j=-1,a.animate=!0)}zh(a.usedTiles,l,n,p);var Qb=d.o;Bh(a,l,m,r,g,D,n,h.f(),function(a){var b;(b=2!=a.V()||Ze(d.a,a.ib()))||(b=a.ib()in Qb.g);b||Qb.f([a,uf(m,a.ma),m.$(a.ma[0]),q,x*r])},this);wh(a,l);yh(a,l);e=this.s;ad(e);dd(e,(v[0]-u[0])/(u[2]-u[0]),(v[1]-u[1])/(u[3]-u[1]));0!==f.rotation&&fd(e,f.rotation);ed(e,a.size[0]*f.resolution/(u[2]-u[0]), +a.size[1]*f.resolution/(u[3]-u[1]));dd(e,-.5,-.5);return!0};k.Cc=function(a,b,c,d){if(this.i){var e=[0,0];sh(this.s,[a[0]/b.size[0],(b.size[1]-a[1])/b.size[1]],e);a=[e[0]*this.l,e[1]*this.l];b=this.f.c.b;b.bindFramebuffer(b.FRAMEBUFFER,this.i);e=new Uint8Array(4);b.readPixels(a[0],a[1],1,1,b.RGBA,b.UNSIGNED_BYTE,e);if(0<e[3])return c.call(d,this.a)}};function Rm(a,b){Gm.call(this,a,b);this.j=!1;this.R=-1;this.D=NaN;this.A=Lb();this.o=this.c=this.C=null}y(Rm,Gm);k=Rm.prototype;k.vh=function(a,b,c){this.o=b;var d=a.viewState,e=this.c;e&&!e.Ya()&&e.Pa(c,d.center,d.resolution,d.rotation,a.size,a.pixelRatio,b.opacity,b.Qc?a.skippedFeatureUids:{})};k.ka=function(){var a=this.c;a&&(wm(a,this.f.c)(),this.c=null);Gm.prototype.ka.call(this)}; +k.ra=function(a,b,c,d){if(this.c&&this.o){var e=b.viewState,f=this.a,g={};return this.c.ra(a,this.f.c,e.center,e.resolution,e.rotation,b.size,b.pixelRatio,this.o.opacity,{},function(a){var b=w(a).toString();if(!(b in g))return g[b]=!0,c.call(d,a,f)})}};k.le=function(a,b){if(this.c&&this.o){var c=b.viewState;return Bm(this.c,a,this.f.c,c.resolution,c.rotation,b.pixelRatio,this.o.opacity,b.skippedFeatureUids)}return!1}; +k.Cc=function(a,b,c,d){a=a.slice();sh(b.pixelToCoordinateMatrix,a,a);if(this.le(a,b))return c.call(d,this.a)};k.wh=function(){uh(this)}; +k.Af=function(a,b,c){function d(a){var b,c=a.ec();c?b=c.call(a,m):(c=e.i)&&(b=c(a,m));if(b){if(b){c=!1;if(Array.isArray(b))for(var d=0,f=b.length;d<f;++d)c=lk(q,a,b[d],jk(m,n),this.wh,this)||c;else c=lk(q,a,b,jk(m,n),this.wh,this)||c;a=c}else a=!1;this.j=this.j||a}}var e=this.a;b=e.ha();xh(a.attributions,b.l);yh(a,b);var f=a.viewHints[0],g=a.viewHints[1],h=e.S,l=e.T;if(!this.j&&!h&&f||!l&&g)return!0;var g=a.extent,h=a.viewState,f=h.projection,m=h.resolution,n=a.pixelRatio,h=e.g,p=e.a,l=xj(e);void 0=== +l&&(l=ik);g=Ob(g,p*m);if(!this.j&&this.D==m&&this.R==h&&this.C==l&&Ub(this.A,g))return!0;this.c&&a.postRenderFunctions.push(wm(this.c,c));this.j=!1;var q=new vm(.5*m/n,g,e.a);b.Pc(g,m,f);if(l){var r=[];b.ub(g,function(a){r.push(a)},this);r.sort(l);r.forEach(d,this)}else b.ub(g,d,this);xm(q,c);this.D=m;this.R=h;this.C=l;this.A=g;this.c=q;return!0};function Sm(a,b){Hh.call(this,0,b);this.b=document.createElement("CANVAS");this.b.style.width="100%";this.b.style.height="100%";this.b.className="ol-unselectable";a.insertBefore(this.b,a.childNodes[0]||null);this.U=this.A=0;this.C=Oe();this.j=!0;this.f=ag(this.b,{antialias:!0,depth:!1,failIfMajorPerformanceCaveat:!0,preserveDrawingBuffer:!1,stencil:!0});this.c=new gm(this.b,this.f);B(this.b,"webglcontextlost",this.Pm,this);B(this.b,"webglcontextrestored",this.Qm,this);this.a=new Ye;this.v=null;this.o= +new Mh(function(a){var b=a[1];a=a[2];var e=b[0]-this.v[0],b=b[1]-this.v[1];return 65536*Math.log(a)+Math.sqrt(e*e+b*b)/a}.bind(this),function(a){return a[0].ib()});this.D=function(){if(!this.o.Ya()){Qh(this.o);var a=Nh(this.o);Qm(this,a[0],a[3],a[4])}return!1}.bind(this);this.l=0;Tm(this)}y(Sm,Hh); +function Qm(a,b,c,d){var e=a.f,f=b.ib();if(Ze(a.a,f))a=a.a.get(f),e.bindTexture(3553,a.pb),9729!=a.Wg&&(e.texParameteri(3553,10240,9729),a.Wg=9729),9729!=a.Yg&&(e.texParameteri(3553,10241,9729),a.Yg=9729);else{var g=e.createTexture();e.bindTexture(3553,g);if(0<d){var h=a.C.canvas,l=a.C;a.A!==c[0]||a.U!==c[1]?(h.width=c[0],h.height=c[1],a.A=c[0],a.U=c[1]):l.clearRect(0,0,c[0],c[1]);l.drawImage(b.$a(),d,d,c[0],c[1],0,0,c[0],c[1]);e.texImage2D(3553,0,6408,6408,5121,h)}else e.texImage2D(3553,0,6408,6408, +5121,b.$a());e.texParameteri(3553,10240,9729);e.texParameteri(3553,10241,9729);e.texParameteri(3553,10242,33071);e.texParameteri(3553,10243,33071);a.a.set(f,{pb:g,Wg:9729,Yg:9729})}}k=Sm.prototype;k.Xe=function(a){return a instanceof cj?new Jm(this,a):a instanceof dj?new Pm(this,a):a instanceof G?new Rm(this,a):null};function Um(a,b,c){var d=a.i;if(ab(d,b)){a=a.c;var e=c.viewState;d.b(new lh(b,d,new Cm(a,e.center,e.resolution,e.rotation,c.size,c.extent,c.pixelRatio),c,null,a))}} +k.ka=function(){var a=this.f;a.isContextLost()||this.a.forEach(function(b){b&&a.deleteTexture(b.pb)});Ta(this.c);Hh.prototype.ka.call(this)};k.Gj=function(a,b){for(var c=this.f,d;1024<this.a.wc()-this.l;){if(d=this.a.b.pc)c.deleteTexture(d.pb);else if(+this.a.b.cc==b.index)break;else--this.l;this.a.pop()}};k.X=function(){return"webgl"};k.Pm=function(a){a.preventDefault();this.a.clear();this.l=0;a=this.g;for(var b in a)a[b].zf()};k.Qm=function(){Tm(this);this.i.render()}; +function Tm(a){a=a.f;a.activeTexture(33984);a.blendFuncSeparate(770,771,1,771);a.disable(2884);a.disable(2929);a.disable(3089);a.disable(2960)} +k.Ce=function(a){var b=this.c,c=this.f;if(c.isContextLost())return!1;if(!a)return this.j&&(this.b.style.display="none",this.j=!1),!1;this.v=a.focus;this.a.set((-a.index).toString(),null);++this.l;Um(this,"precompose",a);var d=[],e=a.layerStatesArray;qb(e);var f=a.viewState.resolution,g,h,l,m;g=0;for(h=e.length;g<h;++g)m=e[g],nh(m,f)&&"ready"==m.R&&(l=Kh(this,m.layer),l.Af(a,m,b)&&d.push(m));e=a.size[0]*a.pixelRatio;f=a.size[1]*a.pixelRatio;if(this.b.width!=e||this.b.height!=f)this.b.width=e,this.b.height= +f;c.bindFramebuffer(36160,null);c.clearColor(0,0,0,0);c.clear(16384);c.enable(3042);c.viewport(0,0,this.b.width,this.b.height);g=0;for(h=d.length;g<h;++g)m=d[g],l=Kh(this,m.layer),l.vh(a,m,b);this.j||(this.b.style.display="",this.j=!0);Ih(a);1024<this.a.wc()-this.l&&a.postRenderFunctions.push(this.Gj.bind(this));this.o.Ya()||(a.postRenderFunctions.push(this.D),a.animate=!0);Um(this,"postcompose",a);Lh(this,a);a.postRenderFunctions.push(Jh)}; +k.ra=function(a,b,c,d,e,f){var g;if(this.f.isContextLost())return!1;var h=b.viewState,l=b.layerStatesArray,m;for(m=l.length-1;0<=m;--m){g=l[m];var n=g.layer;if(nh(g,h.resolution)&&e.call(f,n)&&(g=Kh(this,n).ra(a,b,c,d)))return g}};k.sh=function(a,b,c,d){var e=!1;if(this.f.isContextLost())return!1;var f=b.viewState,g=b.layerStatesArray,h;for(h=g.length-1;0<=h;--h){var l=g[h],m=l.layer;if(nh(l,f.resolution)&&c.call(d,m)&&(e=Kh(this,m).le(a,b)))return!0}return e}; +k.rh=function(a,b,c,d,e){if(this.f.isContextLost())return!1;var f=b.viewState,g,h=b.layerStatesArray,l;for(l=h.length-1;0<=l;--l){g=h[l];var m=g.layer;if(nh(g,f.resolution)&&e.call(d,m)&&(g=Kh(this,m).Cc(a,b,c,d)))return g}};var Vm=["canvas","webgl","dom"]; +function Q(a){eb.call(this);var b=Wm(a);this.Hb=void 0!==a.loadTilesWhileAnimating?a.loadTilesWhileAnimating:!1;this.Hc=void 0!==a.loadTilesWhileInteracting?a.loadTilesWhileInteracting:!1;this.Oe=void 0!==a.pixelRatio?a.pixelRatio:gg;this.Ne=b.logos;this.Y=function(){this.i=void 0;this.Uo.call(this,Date.now())}.bind(this);this.Sa=Xc();this.Pe=Xc();this.qb=0;this.f=null;this.Aa=Lb();this.D=this.S=null;this.a=document.createElement("DIV");this.a.className="ol-viewport"+(lg?" ol-touch":"");this.a.style.position= +"relative";this.a.style.overflow="hidden";this.a.style.width="100%";this.a.style.height="100%";this.a.style.msTouchAction="none";this.a.style.touchAction="none";this.A=document.createElement("DIV");this.A.className="ol-overlaycontainer";this.a.appendChild(this.A);this.v=document.createElement("DIV");this.v.className="ol-overlaycontainer-stopevent";a=["click","dblclick","mousedown","touchstart","mspointerdown",eh,"mousewheel","wheel"];for(var c=0,d=a.length;c<d;++c)B(this.v,a[c],Ya);this.a.appendChild(this.v); +this.za=new Xg(this);for(var e in hh)B(this.za,hh[e],this.Pg,this);this.ia=b.keyboardEventTarget;this.s=null;B(this.a,"wheel",this.Oc,this);B(this.a,"mousewheel",this.Oc,this);this.o=b.controls;this.l=b.interactions;this.j=b.overlays;this.Cf={};this.C=new b.Wo(this.a,this);this.T=null;this.R=[];this.ta=[];this.qa=new Rh(this.Bk.bind(this),this.hl.bind(this));this.Ee={};B(this,gb("layergroup"),this.Ok,this);B(this,gb("view"),this.il,this);B(this,gb("size"),this.el,this);B(this,gb("target"),this.gl, +this);this.G(b.values);this.o.forEach(function(a){a.setMap(this)},this);B(this.o,"add",function(a){a.element.setMap(this)},this);B(this.o,"remove",function(a){a.element.setMap(null)},this);this.l.forEach(function(a){a.setMap(this)},this);B(this.l,"add",function(a){a.element.setMap(this)},this);B(this.l,"remove",function(a){a.element.setMap(null)},this);this.j.forEach(this.mg,this);B(this.j,"add",function(a){this.mg(a.element)},this);B(this.j,"remove",function(a){var b=a.element.Xa();void 0!==b&&delete this.Cf[b.toString()]; +a.element.setMap(null)},this)}y(Q,eb);k=Q.prototype;k.uj=function(a){this.o.push(a)};k.vj=function(a){this.l.push(a)};k.kg=function(a){this.xc().Tc().push(a)};k.lg=function(a){this.j.push(a)};k.mg=function(a){var b=a.Xa();void 0!==b&&(this.Cf[b.toString()]=a);a.setMap(this)};k.Wa=function(a){this.render();Array.prototype.push.apply(this.R,arguments)}; +k.ka=function(){Ta(this.za);Ta(this.C);Qa(this.a,"wheel",this.Oc,this);Qa(this.a,"mousewheel",this.Oc,this);void 0!==this.c&&(pa.removeEventListener("resize",this.c,!1),this.c=void 0);this.i&&(pa.cancelAnimationFrame(this.i),this.i=void 0);this.fh(null);eb.prototype.ka.call(this)};k.kd=function(a,b,c,d,e){if(this.f)return a=this.Ma(a),this.C.ra(a,this.f,b,void 0!==c?c:null,void 0!==d?d:qc,void 0!==e?e:null)}; +k.Tl=function(a,b,c,d,e){if(this.f)return this.C.rh(a,this.f,b,void 0!==c?c:null,void 0!==d?d:qc,void 0!==e?e:null)};k.kl=function(a,b,c){if(!this.f)return!1;a=this.Ma(a);return this.C.sh(a,this.f,void 0!==b?b:qc,void 0!==c?c:null)};k.Wj=function(a){return this.Ma(this.Td(a))};k.Td=function(a){var b=this.a.getBoundingClientRect();a=a.changedTouches?a.changedTouches[0]:a;return[a.clientX-b.left,a.clientY-b.top]};k.tf=function(){return this.get("target")}; +k.yc=function(){var a=this.tf();return void 0!==a?"string"===typeof a?document.getElementById(a):a:null};k.Ma=function(a){var b=this.f;return b?(a=a.slice(),sh(b.pixelToCoordinateMatrix,a,a)):null};k.Uj=function(){return this.o};k.nk=function(){return this.j};k.mk=function(a){a=this.Cf[a.toString()];return void 0!==a?a:null};k.ak=function(){return this.l};k.xc=function(){return this.get("layergroup")};k.eh=function(){return this.xc().Tc()}; +k.Ga=function(a){var b=this.f;return b?(a=a.slice(0,2),sh(b.coordinateToPixelMatrix,a,a)):null};k.Za=function(){return this.get("size")};k.aa=function(){return this.get("view")};k.Dk=function(){return this.a};k.Bk=function(a,b,c,d){var e=this.f;if(!(e&&b in e.wantedTiles&&e.wantedTiles[b][a.ma.toString()]))return Infinity;a=c[0]-e.focus[0];c=c[1]-e.focus[1];return 65536*Math.log(d)+Math.sqrt(a*a+c*c)/d};k.Oc=function(a,b){var c=new Vg(b||a.type,this,a);this.Pg(c)}; +k.Pg=function(a){if(this.f){this.T=a.coordinate;a.frameState=this.f;var b=this.l.a,c;if(!1!==this.b(a))for(c=b.length-1;0<=c;c--){var d=b[c];if(d.f()&&!d.handleEvent(a))break}}};k.cl=function(){var a=this.f,b=this.qa;if(!b.Ya()){var c=16,d=c;if(a){var e=a.viewHints;e[0]&&(c=this.Hb?8:0,d=2);e[1]&&(c=this.Hc?8:0,d=2)}b.i<c&&(Qh(b),Sh(b,c,d))}b=this.ta;c=0;for(d=b.length;c<d;++c)b[c](this,a);b.length=0};k.el=function(){this.render()}; +k.gl=function(){var a;this.tf()&&(a=this.yc());if(this.s){for(var b=0,c=this.s.length;b<c;++b)Ka(this.s[b]);this.s=null}a?(a.appendChild(this.a),a=this.ia?this.ia:a,this.s=[B(a,"keydown",this.Oc,this),B(a,"keypress",this.Oc,this)],this.c||(this.c=this.Xc.bind(this),pa.addEventListener("resize",this.c,!1))):(Ue(this.a),void 0!==this.c&&(pa.removeEventListener("resize",this.c,!1),this.c=void 0));this.Xc()};k.hl=function(){this.render()};k.jl=function(){this.render()}; +k.il=function(){this.S&&(Ka(this.S),this.S=null);var a=this.aa();a&&(this.S=B(a,"propertychange",this.jl,this));this.render()};k.Pk=function(){this.render()};k.Qk=function(){this.render()};k.Ok=function(){this.D&&(this.D.forEach(Ka),this.D=null);var a=this.xc();a&&(this.D=[B(a,"propertychange",this.Qk,this),B(a,"change",this.Pk,this)]);this.render()};k.Vo=function(){this.i&&pa.cancelAnimationFrame(this.i);this.Y()};k.render=function(){void 0===this.i&&(this.i=pa.requestAnimationFrame(this.Y))}; +k.Oo=function(a){return this.o.remove(a)};k.Po=function(a){return this.l.remove(a)};k.Ro=function(a){return this.xc().Tc().remove(a)};k.So=function(a){return this.j.remove(a)}; +k.Uo=function(a){var b,c,d,e=this.Za(),f=this.aa(),g=Lb(),h=null;if(void 0!==e&&0<e[0]&&0<e[1]&&f&&Wd(f)){var h=Sd(f,this.f?this.f.viewHints:void 0),l=this.xc().hf(),m={};b=0;for(c=l.length;b<c;++b)m[w(l[b].layer)]=l[b];d=f.V();h={animate:!1,attributions:{},coordinateToPixelMatrix:this.Sa,extent:g,focus:this.T?this.T:d.center,index:this.qb++,layerStates:m,layerStatesArray:l,logos:Ea({},this.Ne),pixelRatio:this.Oe,pixelToCoordinateMatrix:this.Pe,postRenderFunctions:[],size:e,skippedFeatureUids:this.Ee, +tileQueue:this.qa,time:a,usedTiles:{},viewState:d,viewHints:h,wantedTiles:{}}}if(h){a=this.R;b=e=0;for(c=a.length;b<c;++b)f=a[b],f(this,h)&&(a[e++]=f);a.length=e;h.extent=lc(d.center,d.resolution,d.rotation,h.size,g)}this.f=h;this.C.Ce(h);h&&(h.animate&&this.render(),Array.prototype.push.apply(this.ta,h.postRenderFunctions),0!==this.R.length||h.viewHints[0]||h.viewHints[1]||$b(h.extent,this.Aa)||(this.b(new We("moveend",this,h)),Pb(h.extent,this.Aa)));this.b(new We("postrender",this,h));Tf(this.cl, +this)};k.ji=function(a){this.set("layergroup",a)};k.Wf=function(a){this.set("size",a)};k.fh=function(a){this.set("target",a)};k.kp=function(a){this.set("view",a)};k.ti=function(a){a=w(a).toString();this.Ee[a]=!0;this.render()}; +k.Xc=function(){var a=this.yc();if(a){var b=pa.getComputedStyle(a);this.Wf([a.offsetWidth-parseFloat(b.borderLeftWidth)-parseFloat(b.paddingLeft)-parseFloat(b.paddingRight)-parseFloat(b.borderRightWidth),a.offsetHeight-parseFloat(b.borderTopWidth)-parseFloat(b.paddingTop)-parseFloat(b.paddingBottom)-parseFloat(b.borderBottomWidth)])}else this.Wf(void 0)};k.wi=function(a){a=w(a).toString();delete this.Ee[a];this.render()}; +function Wm(a){var b=null;void 0!==a.keyboardEventTarget&&(b="string"===typeof a.keyboardEventTarget?document.getElementById(a.keyboardEventTarget):a.keyboardEventTarget);var c={},d={};if(void 0===a.logo||"boolean"===typeof a.logo&&a.logo)d["data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAAHGAAABxgEXwfpGAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAhNQTFRF////AP//AICAgP//AFVVQECA////K1VVSbbbYL/fJ05idsTYJFtbbcjbJllmZszWWMTOIFhoHlNiZszTa9DdUcHNHlNlV8XRIVdiasrUHlZjIVZjaMnVH1RlIFRkH1RkH1ZlasvYasvXVsPQH1VkacnVa8vWIVZjIFRjVMPQa8rXIVVkXsXRsNveIFVkIFZlIVVj3eDeh6GmbMvXH1ZkIFRka8rWbMvXIFVkIFVjIFVkbMvWH1VjbMvWIFVlbcvWIFVla8vVIFVkbMvWbMvVH1VkbMvWIFVlbcvWIFVkbcvVbMvWjNPbIFVkU8LPwMzNIFVkbczWIFVkbsvWbMvXIFVkRnB8bcvW2+TkW8XRIFVkIlZlJVloJlpoKlxrLl9tMmJwOWd0Omh1RXF8TneCT3iDUHiDU8LPVMLPVcLPVcPQVsPPVsPQV8PQWMTQWsTQW8TQXMXSXsXRX4SNX8bSYMfTYcfTYsfTY8jUZcfSZsnUaIqTacrVasrVa8jTa8rWbI2VbMvWbcvWdJObdcvUdszUd8vVeJaee87Yfc3WgJyjhqGnitDYjaarldPZnrK2oNbborW5o9bbo9fbpLa6q9ndrL3ArtndscDDutzfu8fJwN7gwt7gxc/QyuHhy+HizeHi0NfX0+Pj19zb1+Tj2uXk29/e3uLg3+Lh3+bl4uXj4ufl4+fl5Ofl5ufl5ujm5+jmySDnBAAAAFp0Uk5TAAECAgMEBAYHCA0NDg4UGRogIiMmKSssLzU7PkJJT1JTVFliY2hrdHZ3foSFhYeJjY2QkpugqbG1tre5w8zQ09XY3uXn6+zx8vT09vf4+Pj5+fr6/P39/f3+gz7SsAAAAVVJREFUOMtjYKA7EBDnwCPLrObS1BRiLoJLnte6CQy8FLHLCzs2QUG4FjZ5GbcmBDDjxJBXDWxCBrb8aM4zbkIDzpLYnAcE9VXlJSWlZRU13koIeW57mGx5XjoMZEUqwxWYQaQbSzLSkYGfKFSe0QMsX5WbjgY0YS4MBplemI4BdGBW+DQ11eZiymfqQuXZIjqwyadPNoSZ4L+0FVM6e+oGI6g8a9iKNT3o8kVzNkzRg5lgl7p4wyRUL9Yt2jAxVh6mQCogae6GmflI8p0r13VFWTHBQ0rWPW7ahgWVcPm+9cuLoyy4kCJDzCm6d8PSFoh0zvQNC5OjDJhQopPPJqph1doJBUD5tnkbZiUEqaCnB3bTqLTFG1bPn71kw4b+GFdpLElKIzRxxgYgWNYc5SCENVHKeUaltHdXx0dZ8uBI1hJ2UUDgq82CM2MwKeibqAvSO7MCABq0wXEPiqWEAAAAAElFTkSuQmCC"]= +"http://openlayers.org/";else{var e=a.logo;"string"===typeof e?d[e]="":e instanceof HTMLElement?d[w(e).toString()]=e:fa(e)&&(d[e.src]=e.href)}e=a.layers instanceof Ti?a.layers:new Ti({layers:a.layers});c.layergroup=e;c.target=a.target;c.view=void 0!==a.view?a.view:new Rd;var e=Hh,f;void 0!==a.renderer?Array.isArray(a.renderer)?f=a.renderer:"string"===typeof a.renderer&&(f=[a.renderer]):f=Vm;var g,h;g=0;for(h=f.length;g<h;++g){var l=f[g];if("canvas"==l){if(ig){e=Ql;break}}else if("dom"==l){e=Yl;break}else if("webgl"== +l&&bg){e=Sm;break}}var m;void 0!==a.controls?m=Array.isArray(a.controls)?new le(a.controls.slice()):a.controls:m=Kf();var n;void 0!==a.interactions?n=Array.isArray(a.interactions)?new le(a.interactions.slice()):a.interactions:n=Si();a=void 0!==a.overlays?Array.isArray(a.overlays)?new le(a.overlays.slice()):a.overlays:new le;return{controls:m,interactions:n,keyboardEventTarget:b,logos:d,overlays:a,Wo:e,values:c}}bj();function Xm(a){eb.call(this);this.j=a.id;this.o=void 0!==a.insertFirst?a.insertFirst:!0;this.s=void 0!==a.stopEvent?a.stopEvent:!0;this.f=document.createElement("DIV");this.f.className="ol-overlay-container";this.f.style.position="absolute";this.autoPan=void 0!==a.autoPan?a.autoPan:!1;this.i=void 0!==a.autoPanAnimation?a.autoPanAnimation:{};this.l=void 0!==a.autoPanMargin?a.autoPanMargin:20;this.a={Md:"",fe:"",De:"",Fe:"",visible:!0};this.c=null;B(this,gb("element"),this.Jk,this);B(this,gb("map"), +this.Vk,this);B(this,gb("offset"),this.Zk,this);B(this,gb("position"),this.al,this);B(this,gb("positioning"),this.bl,this);void 0!==a.element&&this.fi(a.element);this.li(void 0!==a.offset?a.offset:[0,0]);this.oi(void 0!==a.positioning?a.positioning:"top-left");void 0!==a.position&&this.uf(a.position)}y(Xm,eb);k=Xm.prototype;k.Sd=function(){return this.get("element")};k.Xa=function(){return this.j};k.he=function(){return this.get("map")};k.Kg=function(){return this.get("offset")};k.gh=function(){return this.get("position")}; +k.Lg=function(){return this.get("positioning")};k.Jk=function(){Ve(this.f);var a=this.Sd();a&&this.f.appendChild(a)};k.Vk=function(){this.c&&(Ue(this.f),Ka(this.c),this.c=null);var a=this.he();a&&(this.c=B(a,"postrender",this.render,this),Ym(this),a=this.s?a.v:a.A,this.o?a.insertBefore(this.f,a.childNodes[0]||null):a.appendChild(this.f))};k.render=function(){Ym(this)};k.Zk=function(){Ym(this)}; +k.al=function(){Ym(this);if(void 0!==this.get("position")&&this.autoPan){var a=this.he();if(void 0!==a&&a.yc()){var b=Zm(a.yc(),a.Za()),c=this.Sd(),d=c.offsetWidth,e=c.currentStyle||pa.getComputedStyle(c),d=d+(parseInt(e.marginLeft,10)+parseInt(e.marginRight,10)),e=c.offsetHeight,f=c.currentStyle||pa.getComputedStyle(c),e=e+(parseInt(f.marginTop,10)+parseInt(f.marginBottom,10)),g=Zm(c,[d,e]),c=this.l;Ub(b,g)||(d=g[0]-b[0],e=b[2]-g[2],f=g[1]-b[1],g=b[3]-g[3],b=[0,0],0>d?b[0]=d-c:0>e&&(b[0]=Math.abs(e)+ +c),0>f?b[1]=f-c:0>g&&(b[1]=Math.abs(g)+c),0===b[0]&&0===b[1])||(c=a.aa().ab(),d=a.Ga(c),b=[d[0]+b[0],d[1]+b[1]],this.i&&(this.i.source=c,a.Wa(ce(this.i))),a.aa().mb(a.Ma(b)))}}};k.bl=function(){Ym(this)};k.fi=function(a){this.set("element",a)};k.setMap=function(a){this.set("map",a)};k.li=function(a){this.set("offset",a)};k.uf=function(a){this.set("position",a)};function Zm(a,b){var c=a.getBoundingClientRect(),d=c.left+pa.pageXOffset,c=c.top+pa.pageYOffset;return[d,c,d+b[0],c+b[1]]} +k.oi=function(a){this.set("positioning",a)};function $m(a,b){a.a.visible!==b&&(a.f.style.display=b?"":"none",a.a.visible=b)} +function Ym(a){var b=a.he(),c=a.gh();if(void 0!==b&&b.f&&void 0!==c){var c=b.Ga(c),d=b.Za(),b=a.f.style,e=a.Kg(),f=a.Lg(),g=e[0],e=e[1];if("bottom-right"==f||"center-right"==f||"top-right"==f)""!==a.a.fe&&(a.a.fe=b.left=""),g=Math.round(d[0]-c[0]-g)+"px",a.a.De!=g&&(a.a.De=b.right=g);else{""!==a.a.De&&(a.a.De=b.right="");if("bottom-center"==f||"center-center"==f||"top-center"==f)g-=a.f.offsetWidth/2;g=Math.round(c[0]+g)+"px";a.a.fe!=g&&(a.a.fe=b.left=g)}if("bottom-left"==f||"bottom-center"==f||"bottom-right"== +f)""!==a.a.Fe&&(a.a.Fe=b.top=""),c=Math.round(d[1]-c[1]-e)+"px",a.a.Md!=c&&(a.a.Md=b.bottom=c);else{""!==a.a.Md&&(a.a.Md=b.bottom="");if("center-left"==f||"center-center"==f||"center-right"==f)e-=a.f.offsetHeight/2;c=Math.round(c[1]+e)+"px";a.a.Fe!=c&&(a.a.Fe=b.top=c)}$m(a,!0)}else $m(a,!1)};function an(a){a=a?a:{};this.l=void 0!==a.collapsed?a.collapsed:!0;this.o=void 0!==a.collapsible?a.collapsible:!0;this.o||(this.l=!1);var b=void 0!==a.className?a.className:"ol-overviewmap",c=void 0!==a.tipLabel?a.tipLabel:"Overview map",d=void 0!==a.collapseLabel?a.collapseLabel:"\u00ab";"string"===typeof d?(this.j=document.createElement("span"),this.j.textContent=d):this.j=d;d=void 0!==a.label?a.label:"\u00bb";"string"===typeof d?(this.v=document.createElement("span"),this.v.textContent=d):this.v= +d;var e=this.o&&!this.l?this.j:this.v,d=document.createElement("button");d.setAttribute("type","button");d.title=c;d.appendChild(e);B(d,"click",this.gm,this);c=document.createElement("DIV");c.className="ol-overviewmap-map";var f=this.f=new Q({controls:new le,interactions:new le,target:c,view:a.view});a.layers&&a.layers.forEach(function(a){f.kg(a)},this);e=document.createElement("DIV");e.className="ol-overviewmap-box";e.style.boxSizing="border-box";this.A=new Xm({position:[0,0],positioning:"bottom-left", +element:e});this.f.lg(this.A);e=document.createElement("div");e.className=b+" ol-unselectable ol-control"+(this.l&&this.o?" ol-collapsed":"")+(this.o?"":" ol-uncollapsible");e.appendChild(c);e.appendChild(d);Xe.call(this,{element:e,render:a.render?a.render:bn,target:a.target})}y(an,Xe);k=an.prototype; +k.setMap=function(a){var b=this.a;a!==b&&(b&&(b=b.aa())&&Qa(b,gb("rotation"),this.de,this),Xe.prototype.setMap.call(this,a),a&&(this.s.push(B(a,"propertychange",this.Wk,this)),0===this.f.eh().dc()&&this.f.ji(a.xc()),a=a.aa()))&&(B(a,gb("rotation"),this.de,this),Wd(a)&&(this.f.Xc(),cn(this)))};k.Wk=function(a){"view"===a.key&&((a=a.oldValue)&&Qa(a,gb("rotation"),this.de,this),a=this.a.aa(),B(a,gb("rotation"),this.de,this))};k.de=function(){this.f.aa().ie(this.a.aa().La())}; +function bn(){var a=this.a,b=this.f;if(a.f&&b.f){var c=a.Za(),a=a.aa().Kc(c),d=b.Za(),c=b.aa().Kc(d),e=b.Ga(fc(a)),f=b.Ga(dc(a)),b=Math.abs(e[0]-f[0]),e=Math.abs(e[1]-f[1]),f=d[0],d=d[1];b<.1*f||e<.1*d||b>.75*f||e>.75*d?cn(this):Ub(c,a)||(a=this.f,c=this.a.aa(),a.aa().mb(c.ab()))}dn(this)}function cn(a){var b=a.a;a=a.f;var c=b.Za(),b=b.aa().Kc(c),c=a.Za();a=a.aa();oc(b,1/(.1*Math.pow(2,Math.log(7.5)/Math.LN2/2)));a.cf(b,c)} +function dn(a){var b=a.a,c=a.f;if(b.f&&c.f){var d=b.Za(),e=b.aa(),f=c.aa();c.Za();var c=e.La(),b=a.A,g=a.A.Sd(),e=e.Kc(d),d=f.$(),f=cc(e),e=ec(e),h;if(a=a.a.aa().ab())h=[f[0]-a[0],f[1]-a[1]],Gb(h,c),Bb(h,a);b.uf(h);g&&(g.style.width=Math.abs((f[0]-e[0])/d)+"px",g.style.height=Math.abs((e[1]-f[1])/d)+"px")}}k.gm=function(a){a.preventDefault();en(this)}; +function en(a){a.element.classList.toggle("ol-collapsed");a.l?Te(a.j,a.v):Te(a.v,a.j);a.l=!a.l;var b=a.f;a.l||b.f||(b.Xc(),cn(a),Pa(b,"postrender",function(){dn(this)},a))}k.fm=function(){return this.o};k.im=function(a){this.o!==a&&(this.o=a,this.element.classList.toggle("ol-uncollapsible"),!a&&this.l&&en(this))};k.hm=function(a){this.o&&this.l!==a&&en(this)};k.em=function(){return this.l};k.pk=function(){return this.f};function fn(a){a=a?a:{};var b=void 0!==a.className?a.className:"ol-scale-line";this.o=document.createElement("DIV");this.o.className=b+"-inner";this.f=document.createElement("DIV");this.f.className=b+" ol-unselectable";this.f.appendChild(this.o);this.v=null;this.j=void 0!==a.minWidth?a.minWidth:64;this.l=!1;this.C=void 0;this.A="";Xe.call(this,{element:this.f,render:a.render?a.render:gn,target:a.target});B(this,gb("units"),this.R,this);this.D(a.units||"metric")}y(fn,Xe);var hn=[1,2,5]; +fn.prototype.wb=function(){return this.get("units")};function gn(a){(a=a.frameState)?this.v=a.viewState:this.v=null;jn(this)}fn.prototype.R=function(){jn(this)};fn.prototype.D=function(a){this.set("units",a)}; +function jn(a){var b=a.v;if(b){var c=b.projection,d=c.$b(),b=c.getPointResolution(b.resolution,b.center)*d,d=a.j*b,c="",e=a.wb();"degrees"==e?(c=uc.degrees,b/=c,d<c/60?(c="\u2033",b*=3600):d<c?(c="\u2032",b*=60):c="\u00b0"):"imperial"==e?.9144>d?(c="in",b/=.0254):1609.344>d?(c="ft",b/=.3048):(c="mi",b/=1609.344):"nautical"==e?(b/=1852,c="nm"):"metric"==e?1>d?(c="mm",b*=1E3):1E3>d?c="m":(c="km",b/=1E3):"us"==e&&(.9144>d?(c="in",b*=39.37):1609.344>d?(c="ft",b/=.30480061):(c="mi",b/=1609.3472));for(var e= +3*Math.floor(Math.log(a.j*b)/Math.log(10)),f;;){f=hn[(e%3+3)%3]*Math.pow(10,Math.floor(e/3));d=Math.round(f/b);if(isNaN(d)){a.f.style.display="none";a.l=!1;return}if(d>=a.j)break;++e}b=f+" "+c;a.A!=b&&(a.o.innerHTML=b,a.A=b);a.C!=d&&(a.o.style.width=d+"px",a.C=d);a.l||(a.f.style.display="",a.l=!0)}else a.l&&(a.f.style.display="none",a.l=!1)};function kn(a){a=a?a:{};this.f=void 0;this.l=ln;this.v=[];this.C=this.j=0;this.T=null;this.ia=!1;this.Y=void 0!==a.duration?a.duration:200;var b=void 0!==a.className?a.className:"ol-zoomslider",c=document.createElement("button");c.setAttribute("type","button");c.className=b+"-thumb ol-unselectable";var d=document.createElement("div");d.className=b+" ol-unselectable ol-control";d.appendChild(c);this.o=new Pg(d);B(this.o,zg,this.Ik,this);B(this.o,Ag,this.Ng,this);B(this.o,Bg,this.Og,this);B(d,"click", +this.Hk,this);B(c,"click",Ya);Xe.call(this,{element:d,render:a.render?a.render:mn})}y(kn,Xe);kn.prototype.ka=function(){Ta(this.o);Xe.prototype.ka.call(this)};var ln=0;k=kn.prototype;k.setMap=function(a){Xe.prototype.setMap.call(this,a);a&&a.render()}; +function mn(a){if(a.frameState){if(!this.ia){var b=this.element,c=b.offsetWidth,d=b.offsetHeight,e=b.firstElementChild,f=pa.getComputedStyle(e),b=e.offsetWidth+parseFloat(f.marginRight)+parseFloat(f.marginLeft),e=e.offsetHeight+parseFloat(f.marginTop)+parseFloat(f.marginBottom);this.T=[b,e];c>d?(this.l=1,this.C=c-b):(this.l=ln,this.j=d-e);this.ia=!0}a=a.frameState.viewState.resolution;a!==this.f&&(this.f=a,nn(this,a))}} +k.Hk=function(a){var b=this.a,c=b.aa(),d=c.$();b.Wa(ee({resolution:d,duration:this.Y,easing:Zd}));a=on(this,sa(1===this.l?(a.offsetX-this.T[0]/2)/this.C:(a.offsetY-this.T[1]/2)/this.j,0,1));c.Ub(c.constrainResolution(a))}; +k.Ik=function(a){if(!this.A&&a.b.target===this.element.firstElementChild&&(Xd(this.a.aa(),1),this.D=a.clientX,this.R=a.clientY,this.A=!0,0===this.v.length)){a=this.Ng;var b=this.Og;this.v.push(B(document,"mousemove",a,this),B(document,"touchmove",a,this),B(document,Ag,a,this),B(document,"mouseup",b,this),B(document,"touchend",b,this),B(document,Bg,b,this))}}; +k.Ng=function(a){if(this.A){var b=this.element.firstElementChild;this.f=on(this,sa(1===this.l?(a.clientX-this.D+parseInt(b.style.left,10))/this.C:(a.clientY-this.R+parseInt(b.style.top,10))/this.j,0,1));this.a.aa().Ub(this.f);nn(this,this.f);this.D=a.clientX;this.R=a.clientY}};k.Og=function(){if(this.A){var a=this.a,b=a.aa();Xd(b,-1);a.Wa(ee({resolution:this.f,duration:this.Y,easing:Zd}));a=b.constrainResolution(this.f);b.Ub(a);this.A=!1;this.R=this.D=void 0;this.v.forEach(Ka);this.v.length=0}}; +function nn(a,b){var c;c=1-Vd(a.a.aa())(b);var d=a.element.firstElementChild;1==a.l?d.style.left=a.C*c+"px":d.style.top=a.j*c+"px"}function on(a,b){return Ud(a.a.aa())(1-b)};function pn(a){a=a?a:{};this.f=a.extent?a.extent:null;var b=void 0!==a.className?a.className:"ol-zoom-extent",c=void 0!==a.label?a.label:"E",d=void 0!==a.tipLabel?a.tipLabel:"Fit to extent",e=document.createElement("button");e.setAttribute("type","button");e.title=d;e.appendChild("string"===typeof c?document.createTextNode(c):c);B(e,"click",this.l,this);c=document.createElement("div");c.className=b+" ol-unselectable ol-control";c.appendChild(e);Xe.call(this,{element:c,target:a.target})}y(pn,Xe); +pn.prototype.l=function(a){a.preventDefault();var b=this.a;a=b.aa();var c=this.f?this.f:a.l.H(),b=b.Za();a.cf(c,b)};function qn(a){eb.call(this);a=a?a:{};this.a=null;B(this,gb("tracking"),this.Il,this);this.rf(void 0!==a.tracking?a.tracking:!1)}y(qn,eb);k=qn.prototype;k.ka=function(){this.rf(!1);eb.prototype.ka.call(this)}; +k.co=function(a){if(null!==a.alpha){var b=wa(a.alpha);this.set("alpha",b);"boolean"===typeof a.absolute&&a.absolute?this.set("heading",b):ea(a.webkitCompassHeading)&&-1!=a.webkitCompassAccuracy&&this.set("heading",wa(a.webkitCompassHeading))}null!==a.beta&&this.set("beta",wa(a.beta));null!==a.gamma&&this.set("gamma",wa(a.gamma));this.u()};k.Oj=function(){return this.get("alpha")};k.Rj=function(){return this.get("beta")};k.Yj=function(){return this.get("gamma")};k.Hl=function(){return this.get("heading")}; +k.$g=function(){return this.get("tracking")};k.Il=function(){if(jg){var a=this.$g();a&&!this.a?this.a=B(pa,"deviceorientation",this.co,this):a||null===this.a||(Ka(this.a),this.a=null)}};k.rf=function(a){this.set("tracking",a)};function rn(){this.defaultDataProjection=null}function sn(a,b,c){var d;c&&(d={dataProjection:c.dataProjection?c.dataProjection:a.Oa(b),featureProjection:c.featureProjection});return tn(a,d)}function tn(a,b){var c;b&&(c={featureProjection:b.featureProjection,dataProjection:b.dataProjection?b.dataProjection:a.defaultDataProjection,rightHanded:b.rightHanded},b.decimals&&(c.decimals=b.decimals));return c} +function un(a,b,c){var d=c?yc(c.featureProjection):null,e=c?yc(c.dataProjection):null,f;d&&e&&!Oc(d,e)?a instanceof Tc?f=(b?a.clone():a).jb(b?d:e,b?e:d):f=Sc(b?a.slice():a,b?d:e,b?e:d):f=a;if(b&&c&&c.decimals){var g=Math.pow(10,c.decimals);a=function(a){for(var b=0,c=a.length;b<c;++b)a[b]=Math.round(a[b]*g)/g;return a};Array.isArray(f)?a(f):f.rc(a)}return f};function vn(){this.defaultDataProjection=null}y(vn,rn);function wn(a){return fa(a)?a:"string"===typeof a?(a=JSON.parse(a))?a:null:null}k=vn.prototype;k.X=function(){return"json"};k.Rb=function(a,b){return this.Uc(wn(a),sn(this,a,b))};k.Fa=function(a,b){return this.Jf(wn(a),sn(this,a,b))};k.Vc=function(a,b){return this.Rh(wn(a),sn(this,a,b))};k.Oa=function(a){return this.Yh(wn(a))};k.Dd=function(a,b){return JSON.stringify(this.Yc(a,b))};k.Xb=function(a,b){return JSON.stringify(this.Ie(a,b))}; +k.Zc=function(a,b){return JSON.stringify(this.Ke(a,b))};function xn(a,b,c,d,e,f){var g=NaN,h=NaN,l=(c-b)/d;if(0!==l)if(1==l)g=a[b],h=a[b+1];else if(2==l)g=(1-e)*a[b]+e*a[b+d],h=(1-e)*a[b+1]+e*a[b+d+1];else{var h=a[b],l=a[b+1],m=0,g=[0],n;for(n=b+d;n<c;n+=d){var p=a[n],q=a[n+1],m=m+Math.sqrt((p-h)*(p-h)+(q-l)*(q-l));g.push(m);h=p;l=q}c=e*m;l=0;m=g.length;for(n=!1;l<m;)e=l+(m-l>>1),h=+ib(g[e],c),0>h?l=e+1:(m=e,n=!h);e=n?l:~l;0>e?(c=(c-g[-e-2])/(g[-e-1]-g[-e-2]),b+=(-e-2)*d,g=za(a[b],a[b+d],c),h=za(a[b+1],a[b+d+1],c)):(g=a[b+e*d],h=a[b+e*d+1])}return f?(f[0]= +g,f[1]=h,f):[g,h]}function yn(a,b,c,d,e,f){if(c==b)return null;if(e<a[b+d-1])return f?(c=a.slice(b,b+d),c[d-1]=e,c):null;if(a[c-1]<e)return f?(c=a.slice(c-d,c),c[d-1]=e,c):null;if(e==a[b+d-1])return a.slice(b,b+d);b/=d;for(c/=d;b<c;)f=b+c>>1,e<a[(f+1)*d-1]?c=f:b=f+1;c=a[b*d-1];if(e==c)return a.slice((b-1)*d,(b-1)*d+d);f=(e-c)/(a[(b+1)*d-1]-c);c=[];var g;for(g=0;g<d-1;++g)c.push(za(a[(b-1)*d+g],a[b*d+g],f));c.push(e);return c} +function zn(a,b,c,d,e,f){var g=0;if(f)return yn(a,g,b[b.length-1],c,d,e);if(d<a[c-1])return e?(a=a.slice(0,c),a[c-1]=d,a):null;if(a[a.length-1]<d)return e?(a=a.slice(a.length-c),a[c-1]=d,a):null;e=0;for(f=b.length;e<f;++e){var h=b[e];if(g!=h){if(d<a[g+c-1])break;if(d<=a[h-1])return yn(a,g,h,c,d,!1);g=h}}return null};function R(a,b){hd.call(this);this.i=null;this.C=this.D=this.j=-1;this.pa(a,b)}y(R,hd);k=R.prototype;k.wj=function(a){this.B?mb(this.B,a):this.B=a.slice();this.u()};k.clone=function(){var a=new R(null);a.ba(this.f,this.B.slice());return a};k.sb=function(a,b,c,d){if(d<Rb(this.H(),a,b))return d;this.C!=this.g&&(this.D=Math.sqrt(od(this.B,0,this.B.length,this.a,0)),this.C=this.g);return qd(this.B,0,this.B.length,this.a,this.D,!1,a,b,c,d)}; +k.Lj=function(a,b){return Fd(this.B,0,this.B.length,this.a,a,b)};k.lm=function(a,b){return"XYM"!=this.f&&"XYZM"!=this.f?null:yn(this.B,0,this.B.length,this.a,a,void 0!==b?b:!1)};k.Z=function(){return vd(this.B,0,this.B.length,this.a)};k.Bg=function(a,b){return xn(this.B,0,this.B.length,this.a,a,b)};k.mm=function(){var a=this.B,b=this.a,c=a[0],d=a[1],e=0,f;for(f=0+b;f<this.B.length;f+=b)var g=a[f],h=a[f+1],e=e+Math.sqrt((g-c)*(g-c)+(h-d)*(h-d)),c=g,d=h;return e}; +function Fj(a){a.j!=a.g&&(a.i=a.Bg(.5,a.i),a.j=a.g);return a.i}k.Nc=function(a){var b=[];b.length=xd(this.B,0,this.B.length,this.a,a,b,0);a=new R(null);a.ba("XY",b);return a};k.X=function(){return"LineString"};k.Ka=function(a){return Gd(this.B,0,this.B.length,this.a,a)};k.pa=function(a,b){a?(kd(this,b,a,1),this.B||(this.B=[]),this.B.length=td(this.B,0,a,this.a),this.u()):this.ba("XY",null)};k.ba=function(a,b){jd(this,a,b);this.u()};function S(a,b){hd.call(this);this.i=[];this.j=this.C=-1;this.pa(a,b)}y(S,hd);k=S.prototype;k.xj=function(a){this.B?mb(this.B,a.la().slice()):this.B=a.la().slice();this.i.push(this.B.length);this.u()};k.clone=function(){var a=new S(null);a.ba(this.f,this.B.slice(),this.i.slice());return a};k.sb=function(a,b,c,d){if(d<Rb(this.H(),a,b))return d;this.j!=this.g&&(this.C=Math.sqrt(pd(this.B,0,this.i,this.a,0)),this.j=this.g);return rd(this.B,0,this.i,this.a,this.C,!1,a,b,c,d)}; +k.om=function(a,b,c){return"XYM"!=this.f&&"XYZM"!=this.f||0===this.B.length?null:zn(this.B,this.i,this.a,a,void 0!==b?b:!1,void 0!==c?c:!1)};k.Z=function(){return wd(this.B,0,this.i,this.a)};k.Db=function(){return this.i};k.fk=function(a){if(0>a||this.i.length<=a)return null;var b=new R(null);b.ba(this.f,this.B.slice(0===a?0:this.i[a-1],this.i[a]));return b}; +k.md=function(){var a=this.B,b=this.i,c=this.f,d=[],e=0,f,g;f=0;for(g=b.length;f<g;++f){var h=b[f],l=new R(null);l.ba(c,a.slice(e,h));d.push(l);e=h}return d};function Gj(a){var b=[],c=a.B,d=0,e=a.i;a=a.a;var f,g;f=0;for(g=e.length;f<g;++f){var h=e[f],d=xn(c,d,h,a,.5);mb(b,d);d=h}return b}k.Nc=function(a){var b=[],c=[],d=this.B,e=this.i,f=this.a,g=0,h=0,l,m;l=0;for(m=e.length;l<m;++l){var n=e[l],h=xd(d,g,n,f,a,b,h);c.push(h);g=n}b.length=h;a=new S(null);a.ba("XY",b,c);return a};k.X=function(){return"MultiLineString"}; +k.Ka=function(a){a:{var b=this.B,c=this.i,d=this.a,e=0,f,g;f=0;for(g=c.length;f<g;++f){if(Gd(b,e,c[f],d,a)){a=!0;break a}e=c[f]}a=!1}return a};k.pa=function(a,b){if(a){kd(this,b,a,2);this.B||(this.B=[]);var c=ud(this.B,0,a,this.a,this.i);this.B.length=0===c.length?0:c[c.length-1];this.u()}else this.ba("XY",null,this.i)};k.ba=function(a,b,c){jd(this,a,b);this.i=c;this.u()}; +function An(a,b){var c=a.f,d=[],e=[],f,g;f=0;for(g=b.length;f<g;++f){var h=b[f];0===f&&(c=h.f);mb(d,h.la());e.push(d.length)}a.ba(c,d,e)};function Bn(a,b){hd.call(this);this.pa(a,b)}y(Bn,hd);k=Bn.prototype;k.zj=function(a){this.B?mb(this.B,a.la()):this.B=a.la().slice();this.u()};k.clone=function(){var a=new Bn(null);a.ba(this.f,this.B.slice());return a};k.sb=function(a,b,c,d){if(d<Rb(this.H(),a,b))return d;var e=this.B,f=this.a,g,h,l;g=0;for(h=e.length;g<h;g+=f)if(l=va(a,b,e[g],e[g+1]),l<d){d=l;for(l=0;l<f;++l)c[l]=e[g+l];c.length=f}return d};k.Z=function(){return vd(this.B,0,this.B.length,this.a)}; +k.rk=function(a){var b=this.B?this.B.length/this.a:0;if(0>a||b<=a)return null;b=new C(null);b.ba(this.f,this.B.slice(a*this.a,(a+1)*this.a));return b};k.je=function(){var a=this.B,b=this.f,c=this.a,d=[],e,f;e=0;for(f=a.length;e<f;e+=c){var g=new C(null);g.ba(b,a.slice(e,e+c));d.push(g)}return d};k.X=function(){return"MultiPoint"};k.Ka=function(a){var b=this.B,c=this.a,d,e,f,g;d=0;for(e=b.length;d<e;d+=c)if(f=b[d],g=b[d+1],Tb(a,f,g))return!0;return!1}; +k.pa=function(a,b){a?(kd(this,b,a,1),this.B||(this.B=[]),this.B.length=td(this.B,0,a,this.a),this.u()):this.ba("XY",null)};k.ba=function(a,b){jd(this,a,b);this.u()};function T(a,b){hd.call(this);this.i=[];this.C=-1;this.D=null;this.T=this.R=this.S=-1;this.j=null;this.pa(a,b)}y(T,hd);k=T.prototype;k.Aj=function(a){if(this.B){var b=this.B.length;mb(this.B,a.la());a=a.Db().slice();var c,d;c=0;for(d=a.length;c<d;++c)a[c]+=b}else this.B=a.la().slice(),a=a.Db().slice(),this.i.push();this.i.push(a);this.u()};k.clone=function(){for(var a=new T(null),b=this.i.length,c=Array(b),d=0;d<b;++d)c[d]=this.i[d].slice();Cn(a,this.f,this.B.slice(),c);return a}; +k.sb=function(a,b,c,d){if(d<Rb(this.H(),a,b))return d;if(this.R!=this.g){var e=this.i,f=0,g=0,h,l;h=0;for(l=e.length;h<l;++h)var m=e[h],g=pd(this.B,f,m,this.a,g),f=m[m.length-1];this.S=Math.sqrt(g);this.R=this.g}e=Hj(this);f=this.i;g=this.a;h=this.S;l=0;var m=[NaN,NaN],n,p;n=0;for(p=f.length;n<p;++n){var q=f[n];d=rd(e,l,q,g,h,!0,a,b,c,d,m);l=q[q.length-1]}return d}; +k.Bc=function(a,b){var c;a:{c=Hj(this);var d=this.i,e=0;if(0!==d.length){var f,g;f=0;for(g=d.length;f<g;++f){var h=d[f];if(Dd(c,e,h,this.a,a,b)){c=!0;break a}e=h[h.length-1]}}c=!1}return c};k.pm=function(){var a=Hj(this),b=this.i,c=0,d=0,e,f;e=0;for(f=b.length;e<f;++e)var g=b[e],d=d+md(a,c,g,this.a),c=g[g.length-1];return d}; +k.Z=function(a){var b;void 0!==a?(b=Hj(this).slice(),Ld(b,this.i,this.a,a)):b=this.B;a=b;b=this.i;var c=this.a,d=0,e=[],f=0,g,h;g=0;for(h=b.length;g<h;++g){var l=b[g];e[f++]=wd(a,d,l,c,e[f]);d=l[l.length-1]}e.length=f;return e}; +function Ij(a){if(a.C!=a.g){var b=a.B,c=a.i,d=a.a,e=0,f=[],g,h;g=0;for(h=c.length;g<h;++g){var l=c[g],e=Yb(b,e,l[0],d);f.push((e[0]+e[2])/2,(e[1]+e[3])/2);e=l[l.length-1]}b=Hj(a);c=a.i;d=a.a;g=0;h=[];l=0;for(e=c.length;l<e;++l){var m=c[l];h=Ed(b,g,m,d,f,2*l,h);g=m[m.length-1]}a.D=h;a.C=a.g}return a.D}k.ck=function(){var a=new Bn(null);a.ba("XY",Ij(this).slice());return a}; +function Hj(a){if(a.T!=a.g){var b=a.B,c;a:{c=a.i;var d,e;d=0;for(e=c.length;d<e;++d)if(!Jd(b,c[d],a.a,void 0)){c=!1;break a}c=!0}c?a.j=b:(a.j=b.slice(),a.j.length=Ld(a.j,a.i,a.a));a.T=a.g}return a.j}k.Nc=function(a){var b=[],c=[],d=this.B,e=this.i,f=this.a;a=Math.sqrt(a);var g=0,h=0,l,m;l=0;for(m=e.length;l<m;++l){var n=e[l],p=[],h=yd(d,g,n,f,a,b,h,p);c.push(p);g=n[n.length-1]}b.length=h;d=new T(null);Cn(d,"XY",b,c);return d}; +k.tk=function(a){if(0>a||this.i.length<=a)return null;var b;0===a?b=0:(b=this.i[a-1],b=b[b.length-1]);a=this.i[a].slice();var c=a[a.length-1];if(0!==b){var d,e;d=0;for(e=a.length;d<e;++d)a[d]-=b}d=new E(null);d.ba(this.f,this.B.slice(b,c),a);return d};k.Wd=function(){var a=this.f,b=this.B,c=this.i,d=[],e=0,f,g,h,l;f=0;for(g=c.length;f<g;++f){var m=c[f].slice(),n=m[m.length-1];if(0!==e)for(h=0,l=m.length;h<l;++h)m[h]-=e;h=new E(null);h.ba(a,b.slice(e,n),m);d.push(h);e=n}return d};k.X=function(){return"MultiPolygon"}; +k.Ka=function(a){a:{var b=Hj(this),c=this.i,d=this.a,e=0,f,g;f=0;for(g=c.length;f<g;++f){var h=c[f];if(Hd(b,e,h,d,a)){a=!0;break a}e=h[h.length-1]}a=!1}return a};k.pa=function(a,b){if(a){kd(this,b,a,3);this.B||(this.B=[]);var c=this.B,d=this.a,e=this.i,f=0,e=e?e:[],g=0,h,l;h=0;for(l=a.length;h<l;++h)f=ud(c,f,a[h],d,e[g]),e[g++]=f,f=f[f.length-1];e.length=g;0===e.length?this.B.length=0:(c=e[e.length-1],this.B.length=0===c.length?0:c[c.length-1]);this.u()}else Cn(this,"XY",null,this.i)}; +function Cn(a,b,c,d){jd(a,b,c);a.i=d;a.u()}function Dn(a,b){var c=a.f,d=[],e=[],f,g,h;f=0;for(g=b.length;f<g;++f){var l=b[f];0===f&&(c=l.f);var m=d.length;h=l.Db();var n,p;n=0;for(p=h.length;n<p;++n)h[n]+=m;mb(d,l.la());e.push(h)}Cn(a,c,d,e)};function En(a){a=a?a:{};this.defaultDataProjection=null;this.b=a.geometryName}y(En,vn); +function Fn(a,b){if(!a)return null;var c;if(ea(a.x)&&ea(a.y))c="Point";else if(a.points)c="MultiPoint";else if(a.paths)c=1===a.paths.length?"LineString":"MultiLineString";else if(a.rings){var d=a.rings,e=Gn(a),f=[];c=[];var g,h;g=0;for(h=d.length;g<h;++g){var l=lb(d[g]);Id(l,0,l.length,e.length)?f.push([d[g]]):c.push(d[g])}for(;c.length;){d=c.shift();e=!1;for(g=f.length-1;0<=g;g--)if(Ub((new zd(f[g][0])).H(),(new zd(d)).H())){f[g].push(d);e=!0;break}e||f.push([d.reverse()])}a=Ea({},a);1===f.length? +(c="Polygon",a.rings=f[0]):(c="MultiPolygon",a.rings=f)}return un((0,Hn[c])(a),!1,b)}function Gn(a){var b="XY";!0===a.hasZ&&!0===a.hasM?b="XYZM":!0===a.hasZ?b="XYZ":!0===a.hasM&&(b="XYM");return b}function In(a){a=a.f;return{hasZ:"XYZ"===a||"XYZM"===a,hasM:"XYM"===a||"XYZM"===a}} +var Hn={Point:function(a){return void 0!==a.m&&void 0!==a.z?new C([a.x,a.y,a.z,a.m],"XYZM"):void 0!==a.z?new C([a.x,a.y,a.z],"XYZ"):void 0!==a.m?new C([a.x,a.y,a.m],"XYM"):new C([a.x,a.y])},LineString:function(a){return new R(a.paths[0],Gn(a))},Polygon:function(a){return new E(a.rings,Gn(a))},MultiPoint:function(a){return new Bn(a.points,Gn(a))},MultiLineString:function(a){return new S(a.paths,Gn(a))},MultiPolygon:function(a){return new T(a.rings,Gn(a))}},Jn={Point:function(a){var b=a.Z();a=a.f;if("XYZ"=== +a)return{x:b[0],y:b[1],z:b[2]};if("XYM"===a)return{x:b[0],y:b[1],m:b[2]};if("XYZM"===a)return{x:b[0],y:b[1],z:b[2],m:b[3]};if("XY"===a)return{x:b[0],y:b[1]}},LineString:function(a){var b=In(a);return{hasZ:b.hasZ,hasM:b.hasM,paths:[a.Z()]}},Polygon:function(a){var b=In(a);return{hasZ:b.hasZ,hasM:b.hasM,rings:a.Z(!1)}},MultiPoint:function(a){var b=In(a);return{hasZ:b.hasZ,hasM:b.hasM,points:a.Z()}},MultiLineString:function(a){var b=In(a);return{hasZ:b.hasZ,hasM:b.hasM,paths:a.Z()}},MultiPolygon:function(a){var b= +In(a);a=a.Z(!1);for(var c=[],d=0;d<a.length;d++)for(var e=a[d].length-1;0<=e;e--)c.push(a[d][e]);return{hasZ:b.hasZ,hasM:b.hasM,rings:c}}};k=En.prototype;k.Uc=function(a,b){var c=Fn(a.geometry,b),d=new Ik;this.b&&d.Ec(this.b);d.Ua(c);b&&b.mf&&a.attributes[b.mf]&&d.mc(a.attributes[b.mf]);a.attributes&&d.G(a.attributes);return d}; +k.Jf=function(a,b){var c=b?b:{};if(a.features){var d=[],e=a.features,f,g;c.mf=a.objectIdFieldName;f=0;for(g=e.length;f<g;++f)d.push(this.Uc(e[f],c));return d}return[this.Uc(a,c)]};k.Rh=function(a,b){return Fn(a,b)};k.Yh=function(a){return a.spatialReference&&a.spatialReference.wkid?yc("EPSG:"+a.spatialReference.wkid):null};function Kn(a,b){return(0,Jn[a.X()])(un(a,!0,b),b)}k.Ke=function(a,b){return Kn(a,tn(this,b))}; +k.Yc=function(a,b){b=tn(this,b);var c={},d=a.W();d&&(c.geometry=Kn(d,b));d=a.O();delete d[a.a];c.attributes=Ha(d)?{}:d;b&&b.featureProjection&&(c.spatialReference={wkid:yc(b.featureProjection).cb.split(":").pop()});return c};k.Ie=function(a,b){b=tn(this,b);var c=[],d,e;d=0;for(e=a.length;d<e;++d)c.push(this.Yc(a[d],b));return{features:c}};function Ln(a){Tc.call(this);this.c=a?a:null;Mn(this)}y(Ln,Tc);function Nn(a){var b=[],c,d;c=0;for(d=a.length;c<d;++c)b.push(a[c].clone());return b}function On(a){var b,c;if(a.c)for(b=0,c=a.c.length;b<c;++b)Qa(a.c[b],"change",a.u,a)}function Mn(a){var b,c;if(a.c)for(b=0,c=a.c.length;b<c;++b)B(a.c[b],"change",a.u,a)}k=Ln.prototype;k.clone=function(){var a=new Ln(null);a.hi(this.c);return a}; +k.sb=function(a,b,c,d){if(d<Rb(this.H(),a,b))return d;var e=this.c,f,g;f=0;for(g=e.length;f<g;++f)d=e[f].sb(a,b,c,d);return d};k.Bc=function(a,b){var c=this.c,d,e;d=0;for(e=c.length;d<e;++d)if(c[d].Bc(a,b))return!0;return!1};k.Od=function(a){Wb(Infinity,Infinity,-Infinity,-Infinity,a);for(var b=this.c,c=0,d=b.length;c<d;++c)ac(a,b[c].H());return a};k.ff=function(){return Nn(this.c)}; +k.od=function(a){this.s!=this.g&&(Fa(this.l),this.o=0,this.s=this.g);if(0>a||0!==this.o&&a<this.o)return this;var b=a.toString();if(this.l.hasOwnProperty(b))return this.l[b];var c=[],d=this.c,e=!1,f,g;f=0;for(g=d.length;f<g;++f){var h=d[f],l=h.od(a);c.push(l);l!==h&&(e=!0)}if(e)return a=new Ln(null),On(a),a.c=c,Mn(a),a.u(),this.l[b]=a;this.o=a;return this};k.X=function(){return"GeometryCollection"};k.Ka=function(a){var b=this.c,c,d;c=0;for(d=b.length;c<d;++c)if(b[c].Ka(a))return!0;return!1}; +k.Ya=function(){return 0===this.c.length};k.rotate=function(a,b){for(var c=this.c,d=0,e=c.length;d<e;++d)c[d].rotate(a,b);this.u()};k.hi=function(a){a=Nn(a);On(this);this.c=a;Mn(this);this.u()};k.rc=function(a){var b=this.c,c,d;c=0;for(d=b.length;c<d;++c)b[c].rc(a);this.u()};k.Sc=function(a,b){var c=this.c,d,e;d=0;for(e=c.length;d<e;++d)c[d].Sc(a,b);this.u()};k.ka=function(){On(this);Tc.prototype.ka.call(this)};function Pn(a){a=a?a:{};this.defaultDataProjection=null;this.defaultDataProjection=yc(a.defaultDataProjection?a.defaultDataProjection:"EPSG:4326");this.b=a.geometryName}y(Pn,vn);function Qn(a,b){return a?un((0,Rn[a.type])(a),!1,b):null}function Sn(a,b){return(0,Tn[a.X()])(un(a,!0,b),b)} +var Rn={Point:function(a){return new C(a.coordinates)},LineString:function(a){return new R(a.coordinates)},Polygon:function(a){return new E(a.coordinates)},MultiPoint:function(a){return new Bn(a.coordinates)},MultiLineString:function(a){return new S(a.coordinates)},MultiPolygon:function(a){return new T(a.coordinates)},GeometryCollection:function(a,b){var c=a.geometries.map(function(a){return Qn(a,b)});return new Ln(c)}},Tn={Point:function(a){return{type:"Point",coordinates:a.Z()}},LineString:function(a){return{type:"LineString", +coordinates:a.Z()}},Polygon:function(a,b){var c;b&&(c=b.rightHanded);return{type:"Polygon",coordinates:a.Z(c)}},MultiPoint:function(a){return{type:"MultiPoint",coordinates:a.Z()}},MultiLineString:function(a){return{type:"MultiLineString",coordinates:a.Z()}},MultiPolygon:function(a,b){var c;b&&(c=b.rightHanded);return{type:"MultiPolygon",coordinates:a.Z(c)}},GeometryCollection:function(a,b){return{type:"GeometryCollection",geometries:a.c.map(function(a){var d=Ea({},b);delete d.featureProjection;return Sn(a, +d)})}},Circle:function(){return{type:"GeometryCollection",geometries:[]}}};k=Pn.prototype;k.Uc=function(a,b){var c=Qn(a.geometry,b),d=new Ik;this.b&&d.Ec(this.b);d.Ua(c);void 0!==a.id&&d.mc(a.id);a.properties&&d.G(a.properties);return d};k.Jf=function(a,b){if("Feature"==a.type)return[this.Uc(a,b)];if("FeatureCollection"==a.type){var c=[],d=a.features,e,f;e=0;for(f=d.length;e<f;++e)c.push(this.Uc(d[e],b));return c}return[]};k.Rh=function(a,b){return Qn(a,b)}; +k.Yh=function(a){return(a=a.crs)?"name"==a.type?yc(a.properties.name):"EPSG"==a.type?yc("EPSG:"+a.properties.code):null:this.defaultDataProjection};k.Yc=function(a,b){b=tn(this,b);var c={type:"Feature"},d=a.Xa();void 0!==d&&(c.id=d);(d=a.W())?c.geometry=Sn(d,b):c.geometry=null;d=a.O();delete d[a.a];Ha(d)?c.properties=null:c.properties=d;return c};k.Ie=function(a,b){b=tn(this,b);var c=[],d,e;d=0;for(e=a.length;d<e;++d)c.push(this.Yc(a[d],b));return{type:"FeatureCollection",features:c}}; +k.Ke=function(a,b){return Sn(a,tn(this,b))};function Un(){this.f=new XMLSerializer;this.defaultDataProjection=null}y(Un,rn);k=Un.prototype;k.X=function(){return"xml"};k.Rb=function(a,b){if(Pk(a))return Vn(this,a,b);if(Qk(a))return this.Ph(a,b);if("string"===typeof a){var c=Rk(a);return Vn(this,c,b)}return null};function Vn(a,b,c){a=Wn(a,b,c);return 0<a.length?a[0]:null}k.Fa=function(a,b){if(Pk(a))return Wn(this,a,b);if(Qk(a))return this.lc(a,b);if("string"===typeof a){var c=Rk(a);return Wn(this,c,b)}return[]}; +function Wn(a,b,c){var d=[];for(b=b.firstChild;b;b=b.nextSibling)b.nodeType==Node.ELEMENT_NODE&&mb(d,a.lc(b,c));return d}k.Vc=function(a,b){if(Pk(a))return this.v(a,b);if(Qk(a)){var c=this.ye(a,[sn(this,a,b?b:{})]);return c?c:null}return"string"===typeof a?(c=Rk(a),this.v(c,b)):null};k.Oa=function(a){return Pk(a)?this.Pf(a):Qk(a)?this.Be(a):"string"===typeof a?(a=Rk(a),this.Pf(a)):null};k.Pf=function(){return this.defaultDataProjection};k.Be=function(){return this.defaultDataProjection}; +k.Dd=function(a,b){var c=this.A(a,b);return this.f.serializeToString(c)};k.Xb=function(a,b){var c=this.a(a,b);return this.f.serializeToString(c)};k.Zc=function(a,b){var c=this.s(a,b);return this.f.serializeToString(c)};function Xn(a){a=a?a:{};this.featureType=a.featureType;this.featureNS=a.featureNS;this.srsName=a.srsName;this.schemaLocation="";this.b={};this.b["http://www.opengis.net/gml"]={featureMember:Uk(Xn.prototype.vd),featureMembers:Uk(Xn.prototype.vd)};Un.call(this)}y(Xn,Un);var Yn=/^[\s\xa0]*$/;k=Xn.prototype; +k.vd=function(a,b){var c=a.localName,d=null;if("FeatureCollection"==c)"http://www.opengis.net/wfs"===a.namespaceURI?d=O([],this.b,a,b,this):d=O(null,this.b,a,b,this);else if("featureMembers"==c||"featureMember"==c){var e=b[0],f=e.featureType,g=e.featureNS,h,l;if(!f&&a.childNodes){f=[];g={};h=0;for(l=a.childNodes.length;h<l;++h){var m=a.childNodes[h];if(1===m.nodeType){var n=m.nodeName.split(":").pop();if(-1===f.indexOf(n)){var p="",q=0,m=m.namespaceURI,r;for(r in g){if(g[r]===m){p=r;break}++q}p|| +(p="p"+q,g[p]=m);f.push(p+":"+n)}}}"featureMember"!=c&&(e.featureType=f,e.featureNS=g)}"string"===typeof g&&(h=g,g={},g.p0=h);var e={},f=Array.isArray(f)?f:[f],u;for(u in g){n={};h=0;for(l=f.length;h<l;++h)(-1===f[h].indexOf(":")?"p0":f[h].split(":")[0])===u&&(n[f[h].split(":").pop()]="featureMembers"==c?Tk(this.If,this):Uk(this.If,this));e[g[u]]=n}"featureMember"==c?d=O(void 0,e,a,b):d=O([],e,a,b)}null===d&&(d=[]);return d}; +k.ye=function(a,b){var c=b[0];c.srsName=a.firstElementChild.getAttribute("srsName");var d=O(null,this.bg,a,b,this);if(d)return un(d,!1,c)}; +k.If=function(a,b){var c,d;(d=a.getAttribute("fid"))||(d=a.getAttributeNS("http://www.opengis.net/gml","id")||"");var e={},f;for(c=a.firstElementChild;c;c=c.nextElementSibling){var g=c.localName;if(0===c.childNodes.length||1===c.childNodes.length&&(3===c.firstChild.nodeType||4===c.firstChild.nodeType)){var h=Nk(c,!1);Yn.test(h)&&(h=void 0);e[g]=h}else"boundedBy"!==g&&(f=g),e[g]=this.ye(c,b)}c=new Ik(e);f&&c.Ec(f);d&&c.mc(d);return c}; +k.Xh=function(a,b){var c=this.xe(a,b);if(c){var d=new C(null);d.ba("XYZ",c);return d}};k.Vh=function(a,b){var c=O([],this.Ui,a,b,this);if(c)return new Bn(c)};k.Uh=function(a,b){var c=O([],this.Ti,a,b,this);if(c){var d=new S(null);An(d,c);return d}};k.Wh=function(a,b){var c=O([],this.Vi,a,b,this);if(c){var d=new T(null);Dn(d,c);return d}};k.Mh=function(a,b){al(this.Yi,a,b,this)};k.Ug=function(a,b){al(this.Ri,a,b,this)};k.Nh=function(a,b){al(this.Zi,a,b,this)}; +k.ze=function(a,b){var c=this.xe(a,b);if(c){var d=new R(null);d.ba("XYZ",c);return d}};k.zo=function(a,b){var c=O(null,this.Fd,a,b,this);if(c)return c};k.Th=function(a,b){var c=this.xe(a,b);if(c){var d=new zd(null);Ad(d,"XYZ",c);return d}};k.Ae=function(a,b){var c=O([null],this.Me,a,b,this);if(c&&c[0]){var d=new E(null),e=c[0],f=[e.length],g,h;g=1;for(h=c.length;g<h;++g)mb(e,c[g]),f.push(e.length);d.ba("XYZ",e,f);return d}};k.xe=function(a,b){return O(null,this.Fd,a,b,this)}; +k.Ui={"http://www.opengis.net/gml":{pointMember:Tk(Xn.prototype.Mh),pointMembers:Tk(Xn.prototype.Mh)}};k.Ti={"http://www.opengis.net/gml":{lineStringMember:Tk(Xn.prototype.Ug),lineStringMembers:Tk(Xn.prototype.Ug)}};k.Vi={"http://www.opengis.net/gml":{polygonMember:Tk(Xn.prototype.Nh),polygonMembers:Tk(Xn.prototype.Nh)}};k.Yi={"http://www.opengis.net/gml":{Point:Tk(Xn.prototype.xe)}};k.Ri={"http://www.opengis.net/gml":{LineString:Tk(Xn.prototype.ze)}};k.Zi={"http://www.opengis.net/gml":{Polygon:Tk(Xn.prototype.Ae)}}; +k.Gd={"http://www.opengis.net/gml":{LinearRing:Uk(Xn.prototype.zo)}};k.lc=function(a,b){var c={featureType:this.featureType,featureNS:this.featureNS};b&&Ea(c,sn(this,a,b));return this.vd(a,[c])||[]};k.Be=function(a){return yc(this.srsName?this.srsName:a.firstElementChild.getAttribute("srsName"))};function Zn(a){a=Nk(a,!1);return $n(a)}function $n(a){if(a=/^\s*(true|1)|(false|0)\s*$/.exec(a))return void 0!==a[1]||!1} +function ao(a){a=Nk(a,!1);if(a=/^\s*(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?))\s*$/.exec(a)){var b=Date.UTC(parseInt(a[1],10),parseInt(a[2],10)-1,parseInt(a[3],10),parseInt(a[4],10),parseInt(a[5],10),parseInt(a[6],10))/1E3;if("Z"!=a[7]){var c="-"==a[8]?-1:1,b=b+60*c*parseInt(a[9],10);void 0!==a[10]&&(b+=3600*c*parseInt(a[10],10))}return b}}function bo(a){a=Nk(a,!1);return co(a)} +function co(a){if(a=/^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*$/i.exec(a))return parseFloat(a[1])}function eo(a){a=Nk(a,!1);return fo(a)}function fo(a){if(a=/^\s*(\d+)\s*$/.exec(a))return parseInt(a[1],10)}function U(a){return Nk(a,!1).trim()}function go(a,b){ho(a,b?"1":"0")}function io(a,b){a.appendChild(Lk.createTextNode(b.toPrecision()))}function jo(a,b){a.appendChild(Lk.createTextNode(b.toString()))}function ho(a,b){a.appendChild(Lk.createTextNode(b))};function ko(a){a=a?a:{};Xn.call(this,a);this.b["http://www.opengis.net/gml"].featureMember=Tk(Xn.prototype.vd);this.schemaLocation=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/2.1.2/feature.xsd"}y(ko,Xn);k=ko.prototype; +k.Qh=function(a,b){var c=Nk(a,!1).replace(/^\s*|\s*$/g,""),d=b[0].srsName,e=a.parentNode.getAttribute("srsDimension"),f="enu";d&&(d=yc(d))&&(f=d.b);c=c.split(/[\s,]+/);d=2;a.getAttribute("srsDimension")?d=fo(a.getAttribute("srsDimension")):a.getAttribute("dimension")?d=fo(a.getAttribute("dimension")):e&&(d=fo(e));for(var g,h,l=[],m=0,n=c.length;m<n;m+=d)e=parseFloat(c[m]),g=parseFloat(c[m+1]),h=3===d?parseFloat(c[m+2]):0,"en"===f.substr(0,2)?l.push(e,g,h):l.push(g,e,h);return l}; +k.wo=function(a,b){var c=O([null],this.Ni,a,b,this);return Wb(c[1][0],c[1][1],c[1][3],c[1][4])};k.ml=function(a,b){var c=O(void 0,this.Gd,a,b,this);c&&b[b.length-1].push(c)};k.eo=function(a,b){var c=O(void 0,this.Gd,a,b,this);c&&(b[b.length-1][0]=c)};k.Fd={"http://www.opengis.net/gml":{coordinates:Uk(ko.prototype.Qh)}};k.Me={"http://www.opengis.net/gml":{innerBoundaryIs:ko.prototype.ml,outerBoundaryIs:ko.prototype.eo}};k.Ni={"http://www.opengis.net/gml":{coordinates:Tk(ko.prototype.Qh)}}; +k.bg={"http://www.opengis.net/gml":{Point:Uk(Xn.prototype.Xh),MultiPoint:Uk(Xn.prototype.Vh),LineString:Uk(Xn.prototype.ze),MultiLineString:Uk(Xn.prototype.Uh),LinearRing:Uk(Xn.prototype.Th),Polygon:Uk(Xn.prototype.Ae),MultiPolygon:Uk(Xn.prototype.Wh),Box:Uk(ko.prototype.wo)}};function lo(a){a=a?a:{};Xn.call(this,a);this.j=void 0!==a.surface?a.surface:!1;this.i=void 0!==a.curve?a.curve:!1;this.l=void 0!==a.multiCurve?a.multiCurve:!0;this.o=void 0!==a.multiSurface?a.multiSurface:!0;this.schemaLocation=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/profiles/gmlsfProfile/1.0.0/gmlsf.xsd"}y(lo,Xn);k=lo.prototype;k.Do=function(a,b){var c=O([],this.Si,a,b,this);if(c){var d=new S(null);An(d,c);return d}}; +k.Eo=function(a,b){var c=O([],this.Wi,a,b,this);if(c){var d=new T(null);Dn(d,c);return d}};k.vg=function(a,b){al(this.Oi,a,b,this)};k.vi=function(a,b){al(this.cj,a,b,this)};k.Ho=function(a,b){return O([null],this.Xi,a,b,this)};k.Jo=function(a,b){return O([null],this.bj,a,b,this)};k.Io=function(a,b){return O([null],this.Me,a,b,this)};k.Co=function(a,b){return O([null],this.Fd,a,b,this)};k.ol=function(a,b){var c=O(void 0,this.Gd,a,b,this);c&&b[b.length-1].push(c)}; +k.Hj=function(a,b){var c=O(void 0,this.Gd,a,b,this);c&&(b[b.length-1][0]=c)};k.Zh=function(a,b){var c=O([null],this.dj,a,b,this);if(c&&c[0]){var d=new E(null),e=c[0],f=[e.length],g,h;g=1;for(h=c.length;g<h;++g)mb(e,c[g]),f.push(e.length);d.ba("XYZ",e,f);return d}};k.Oh=function(a,b){var c=O([null],this.Pi,a,b,this);if(c){var d=new R(null);d.ba("XYZ",c);return d}};k.yo=function(a,b){var c=O([null],this.Qi,a,b,this);return Wb(c[1][0],c[1][1],c[2][0],c[2][1])}; +k.Ao=function(a,b){for(var c=Nk(a,!1),d=/^\s*([+\-]?\d*\.?\d+(?:[eE][+\-]?\d+)?)\s*/,e=[],f;f=d.exec(c);)e.push(parseFloat(f[1])),c=c.substr(f[0].length);if(""===c){c=b[0].srsName;d="enu";c&&(d=yc(c).b);if("neu"===d)for(c=0,d=e.length;c<d;c+=3)f=e[c],e[c]=e[c+1],e[c+1]=f;c=e.length;2==c&&e.push(0);return 0===c?void 0:e}}; +k.Mf=function(a,b){var c=Nk(a,!1).replace(/^\s*|\s*$/g,""),d=b[0].srsName,e=a.parentNode.getAttribute("srsDimension"),f="enu";d&&(f=yc(d).b);c=c.split(/\s+/);d=2;a.getAttribute("srsDimension")?d=fo(a.getAttribute("srsDimension")):a.getAttribute("dimension")?d=fo(a.getAttribute("dimension")):e&&(d=fo(e));for(var g,h,l=[],m=0,n=c.length;m<n;m+=d)e=parseFloat(c[m]),g=parseFloat(c[m+1]),h=3===d?parseFloat(c[m+2]):0,"en"===f.substr(0,2)?l.push(e,g,h):l.push(g,e,h);return l}; +k.Fd={"http://www.opengis.net/gml":{pos:Uk(lo.prototype.Ao),posList:Uk(lo.prototype.Mf)}};k.Me={"http://www.opengis.net/gml":{interior:lo.prototype.ol,exterior:lo.prototype.Hj}}; +k.bg={"http://www.opengis.net/gml":{Point:Uk(Xn.prototype.Xh),MultiPoint:Uk(Xn.prototype.Vh),LineString:Uk(Xn.prototype.ze),MultiLineString:Uk(Xn.prototype.Uh),LinearRing:Uk(Xn.prototype.Th),Polygon:Uk(Xn.prototype.Ae),MultiPolygon:Uk(Xn.prototype.Wh),Surface:Uk(lo.prototype.Zh),MultiSurface:Uk(lo.prototype.Eo),Curve:Uk(lo.prototype.Oh),MultiCurve:Uk(lo.prototype.Do),Envelope:Uk(lo.prototype.yo)}};k.Si={"http://www.opengis.net/gml":{curveMember:Tk(lo.prototype.vg),curveMembers:Tk(lo.prototype.vg)}}; +k.Wi={"http://www.opengis.net/gml":{surfaceMember:Tk(lo.prototype.vi),surfaceMembers:Tk(lo.prototype.vi)}};k.Oi={"http://www.opengis.net/gml":{LineString:Tk(Xn.prototype.ze),Curve:Tk(lo.prototype.Oh)}};k.cj={"http://www.opengis.net/gml":{Polygon:Tk(Xn.prototype.Ae),Surface:Tk(lo.prototype.Zh)}};k.dj={"http://www.opengis.net/gml":{patches:Uk(lo.prototype.Ho)}};k.Pi={"http://www.opengis.net/gml":{segments:Uk(lo.prototype.Jo)}};k.Qi={"http://www.opengis.net/gml":{lowerCorner:Tk(lo.prototype.Mf),upperCorner:Tk(lo.prototype.Mf)}}; +k.Xi={"http://www.opengis.net/gml":{PolygonPatch:Uk(lo.prototype.Io)}};k.bj={"http://www.opengis.net/gml":{LineStringSegment:Uk(lo.prototype.Co)}};function mo(a,b,c){c=c[c.length-1].srsName;b=b.Z();for(var d=b.length,e=Array(d),f,g=0;g<d;++g){f=b[g];var h=g,l="enu";c&&(l=yc(c).b);e[h]="en"===l.substr(0,2)?f[0]+" "+f[1]:f[1]+" "+f[0]}ho(a,e.join(" "))} +k.Ji=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);d=Mk(a.namespaceURI,"pos");a.appendChild(d);c=c[c.length-1].srsName;a="enu";c&&(a=yc(c).b);b=b.Z();ho(d,"en"===a.substr(0,2)?b[0]+" "+b[1]:b[1]+" "+b[0])};var no={"http://www.opengis.net/gml":{lowerCorner:L(ho),upperCorner:L(ho)}};k=lo.prototype;k.xp=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);bl({node:a},no,Zk,[b[0]+" "+b[1],b[2]+" "+b[3]],c,["lowerCorner","upperCorner"],this)}; +k.Gi=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);d=Mk(a.namespaceURI,"posList");a.appendChild(d);mo(d,b,c)};k.aj=function(a,b){var c=b[b.length-1],d=c.node,e=c.exteriorWritten;void 0===e&&(c.exteriorWritten=!0);return Mk(d.namespaceURI,void 0!==e?"interior":"exterior")}; +k.Le=function(a,b,c){var d=c[c.length-1].srsName;"PolygonPatch"!==a.nodeName&&d&&a.setAttribute("srsName",d);"Polygon"===a.nodeName||"PolygonPatch"===a.nodeName?(b=b.Vd(),bl({node:a,srsName:d},oo,this.aj,b,c,void 0,this)):"Surface"===a.nodeName&&(d=Mk(a.namespaceURI,"patches"),a.appendChild(d),a=Mk(d.namespaceURI,"PolygonPatch"),d.appendChild(a),this.Le(a,b,c))}; +k.Ge=function(a,b,c){var d=c[c.length-1].srsName;"LineStringSegment"!==a.nodeName&&d&&a.setAttribute("srsName",d);"LineString"===a.nodeName||"LineStringSegment"===a.nodeName?(d=Mk(a.namespaceURI,"posList"),a.appendChild(d),mo(d,b,c)):"Curve"===a.nodeName&&(d=Mk(a.namespaceURI,"segments"),a.appendChild(d),a=Mk(d.namespaceURI,"LineStringSegment"),d.appendChild(a),this.Ge(a,b,c))}; +k.Ii=function(a,b,c){var d=c[c.length-1],e=d.srsName,d=d.surface;e&&a.setAttribute("srsName",e);b=b.Wd();bl({node:a,srsName:e,surface:d},po,this.c,b,c,void 0,this)};k.yp=function(a,b,c){var d=c[c.length-1].srsName;d&&a.setAttribute("srsName",d);b=b.je();bl({node:a,srsName:d},qo,Xk("pointMember"),b,c,void 0,this)};k.Hi=function(a,b,c){var d=c[c.length-1],e=d.srsName,d=d.curve;e&&a.setAttribute("srsName",e);b=b.md();bl({node:a,srsName:e,curve:d},ro,this.c,b,c,void 0,this)}; +k.Ki=function(a,b,c){var d=Mk(a.namespaceURI,"LinearRing");a.appendChild(d);this.Gi(d,b,c)};k.Li=function(a,b,c){var d=this.g(b,c);d&&(a.appendChild(d),this.Le(d,b,c))};k.zp=function(a,b,c){var d=Mk(a.namespaceURI,"Point");a.appendChild(d);this.Ji(d,b,c)};k.Fi=function(a,b,c){var d=this.g(b,c);d&&(a.appendChild(d),this.Ge(d,b,c))}; +k.Je=function(a,b,c){var d=c[c.length-1],e=Ea({},d);e.node=a;var f;Array.isArray(b)?d.dataProjection?f=Sc(b,d.featureProjection,d.dataProjection):f=b:f=un(b,!0,d);bl(e,so,this.g,[f],c,void 0,this)}; +k.Bi=function(a,b,c){var d=b.Xa();d&&a.setAttribute("fid",d);var d=c[c.length-1],e=d.featureNS,f=b.a;d.Dc||(d.Dc={},d.Dc[e]={});var g=b.O();b=[];var h=[],l;for(l in g){var m=g[l];null!==m&&(b.push(l),h.push(m),l==f||m instanceof Tc?l in d.Dc[e]||(d.Dc[e][l]=L(this.Je,this)):l in d.Dc[e]||(d.Dc[e][l]=L(ho)))}l=Ea({},d);l.node=a;bl(l,d.Dc,Xk(void 0,e),h,c,b)}; +var po={"http://www.opengis.net/gml":{surfaceMember:L(lo.prototype.Li),polygonMember:L(lo.prototype.Li)}},qo={"http://www.opengis.net/gml":{pointMember:L(lo.prototype.zp)}},ro={"http://www.opengis.net/gml":{lineStringMember:L(lo.prototype.Fi),curveMember:L(lo.prototype.Fi)}},oo={"http://www.opengis.net/gml":{exterior:L(lo.prototype.Ki),interior:L(lo.prototype.Ki)}},so={"http://www.opengis.net/gml":{Curve:L(lo.prototype.Ge),MultiCurve:L(lo.prototype.Hi),Point:L(lo.prototype.Ji),MultiPoint:L(lo.prototype.yp), +LineString:L(lo.prototype.Ge),MultiLineString:L(lo.prototype.Hi),LinearRing:L(lo.prototype.Gi),Polygon:L(lo.prototype.Le),MultiPolygon:L(lo.prototype.Ii),Surface:L(lo.prototype.Le),MultiSurface:L(lo.prototype.Ii),Envelope:L(lo.prototype.xp)}},to={MultiLineString:"lineStringMember",MultiCurve:"curveMember",MultiPolygon:"polygonMember",MultiSurface:"surfaceMember"};lo.prototype.c=function(a,b){return Mk("http://www.opengis.net/gml",to[b[b.length-1].node.nodeName])}; +lo.prototype.g=function(a,b){var c=b[b.length-1],d=c.multiSurface,e=c.surface,f=c.curve,c=c.multiCurve,g;Array.isArray(a)?g="Envelope":(g=a.X(),"MultiPolygon"===g&&!0===d?g="MultiSurface":"Polygon"===g&&!0===e?g="Surface":"LineString"===g&&!0===f?g="Curve":"MultiLineString"===g&&!0===c&&(g="MultiCurve"));return Mk("http://www.opengis.net/gml",g)}; +lo.prototype.s=function(a,b){b=tn(this,b);var c=Mk("http://www.opengis.net/gml","geom"),d={node:c,srsName:this.srsName,curve:this.i,surface:this.j,multiSurface:this.o,multiCurve:this.l};b&&Ea(d,b);this.Je(c,a,[d]);return c}; +lo.prototype.a=function(a,b){b=tn(this,b);var c=Mk("http://www.opengis.net/gml","featureMembers");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.schemaLocation);var d={srsName:this.srsName,curve:this.i,surface:this.j,multiSurface:this.o,multiCurve:this.l,featureNS:this.featureNS,featureType:this.featureType};b&&Ea(d,b);var d=[d],e=d[d.length-1],f=e.featureType,g=e.featureNS,h={};h[g]={};h[g][f]=L(this.Bi,this);e=Ea({},e);e.node=c;bl(e,h,Xk(f,g),a,d);return c};function uo(a){a=a?a:{};Un.call(this);this.defaultDataProjection=yc("EPSG:4326");this.b=a.readExtensions}y(uo,Un);var vo=[null,"http://www.topografix.com/GPX/1/0","http://www.topografix.com/GPX/1/1"];function wo(a,b,c){a.push(parseFloat(b.getAttribute("lon")),parseFloat(b.getAttribute("lat")));"ele"in c?(a.push(c.ele),delete c.ele):a.push(0);"time"in c?(a.push(c.time),delete c.time):a.push(0);return a}function xo(a,b){var c=b[b.length-1],d=a.getAttribute("href");null!==d&&(c.link=d);al(yo,a,b)} +function zo(a,b){b[b.length-1].extensionsNode_=a}function Ao(a,b){var c=b[0],d=O({flatCoordinates:[]},Bo,a,b);if(d){var e=d.flatCoordinates;delete d.flatCoordinates;var f=new R(null);f.ba("XYZM",e);un(f,!1,c);c=new Ik(f);c.G(d);return c}}function Co(a,b){var c=b[0],d=O({flatCoordinates:[],ends:[]},Do,a,b);if(d){var e=d.flatCoordinates;delete d.flatCoordinates;var f=d.ends;delete d.ends;var g=new S(null);g.ba("XYZM",e,f);un(g,!1,c);c=new Ik(g);c.G(d);return c}} +function Eo(a,b){var c=b[0],d=O({},Fo,a,b);if(d){var e=wo([],a,d),e=new C(e,"XYZM");un(e,!1,c);c=new Ik(e);c.G(d);return c}} +var Go={rte:Ao,trk:Co,wpt:Eo},Ho=M(vo,{rte:Tk(Ao),trk:Tk(Co),wpt:Tk(Eo)}),yo=M(vo,{text:J(U,"linkText"),type:J(U,"linkType")}),Bo=M(vo,{name:J(U),cmt:J(U),desc:J(U),src:J(U),link:xo,number:J(eo),extensions:zo,type:J(U),rtept:function(a,b){var c=O({},Io,a,b);c&&wo(b[b.length-1].flatCoordinates,a,c)}}),Io=M(vo,{ele:J(bo),time:J(ao)}),Do=M(vo,{name:J(U),cmt:J(U),desc:J(U),src:J(U),link:xo,number:J(eo),type:J(U),extensions:zo,trkseg:function(a,b){var c=b[b.length-1];al(Jo,a,b);c.ends.push(c.flatCoordinates.length)}}), +Jo=M(vo,{trkpt:function(a,b){var c=O({},Ko,a,b);c&&wo(b[b.length-1].flatCoordinates,a,c)}}),Ko=M(vo,{ele:J(bo),time:J(ao)}),Fo=M(vo,{ele:J(bo),time:J(ao),magvar:J(bo),geoidheight:J(bo),name:J(U),cmt:J(U),desc:J(U),src:J(U),link:xo,sym:J(U),type:J(U),fix:J(U),sat:J(eo),hdop:J(bo),vdop:J(bo),pdop:J(bo),ageofdgpsdata:J(bo),dgpsid:J(eo),extensions:zo}); +function Lo(a,b){b||(b=[]);for(var c=0,d=b.length;c<d;++c){var e=b[c];if(a.b){var f=e.get("extensionsNode_")||null;a.b(e,f)}e.set("extensionsNode_",void 0)}}uo.prototype.Ph=function(a,b){if(!jb(vo,a.namespaceURI))return null;var c=Go[a.localName];if(!c)return null;c=c(a,[sn(this,a,b)]);if(!c)return null;Lo(this,[c]);return c};uo.prototype.lc=function(a,b){if(!jb(vo,a.namespaceURI))return[];if("gpx"==a.localName){var c=O([],Ho,a,[sn(this,a,b)]);if(c)return Lo(this,c),c}return[]}; +function Mo(a,b,c){a.setAttribute("href",b);b=c[c.length-1].properties;bl({node:a},No,Zk,[b.linkText,b.linkType],c,Oo)}function Po(a,b,c){var d=c[c.length-1],e=d.node.namespaceURI,f=d.properties;a.setAttributeNS(null,"lat",b[1]);a.setAttributeNS(null,"lon",b[0]);switch(d.geometryLayout){case "XYZM":0!==b[3]&&(f.time=b[3]);case "XYZ":0!==b[2]&&(f.ele=b[2]);break;case "XYM":0!==b[2]&&(f.time=b[2])}b="rtept"==a.nodeName?Qo[e]:Ro[e];d=$k(f,b);bl({node:a,properties:f},So,Zk,d,c,b)} +var Oo=["text","type"],No=M(vo,{text:L(ho),type:L(ho)}),To=M(vo,"name cmt desc src link number type rtept".split(" ")),Uo=M(vo,{name:L(ho),cmt:L(ho),desc:L(ho),src:L(ho),link:L(Mo),number:L(jo),type:L(ho),rtept:Wk(L(Po))}),Qo=M(vo,["ele","time"]),Vo=M(vo,"name cmt desc src link number type trkseg".split(" ")),Yo=M(vo,{name:L(ho),cmt:L(ho),desc:L(ho),src:L(ho),link:L(Mo),number:L(jo),type:L(ho),trkseg:Wk(L(function(a,b,c){bl({node:a,geometryLayout:b.f,properties:{}},Wo,Xo,b.Z(),c)}))}),Xo=Xk("trkpt"), +Wo=M(vo,{trkpt:L(Po)}),Ro=M(vo,"ele time magvar geoidheight name cmt desc src link sym type fix sat hdop vdop pdop ageofdgpsdata dgpsid".split(" ")),So=M(vo,{ele:L(io),time:L(function(a,b){var c=new Date(1E3*b);a.appendChild(Lk.createTextNode(c.getUTCFullYear()+"-"+zb(c.getUTCMonth()+1)+"-"+zb(c.getUTCDate())+"T"+zb(c.getUTCHours())+":"+zb(c.getUTCMinutes())+":"+zb(c.getUTCSeconds())+"Z"))}),magvar:L(io),geoidheight:L(io),name:L(ho),cmt:L(ho),desc:L(ho),src:L(ho),link:L(Mo),sym:L(ho),type:L(ho),fix:L(ho), +sat:L(jo),hdop:L(io),vdop:L(io),pdop:L(io),ageofdgpsdata:L(io),dgpsid:L(jo)}),Zo={Point:"wpt",LineString:"rte",MultiLineString:"trk"};function $o(a,b){var c=a.W();if(c&&(c=Zo[c.X()]))return Mk(b[b.length-1].node.namespaceURI,c)} +var ap=M(vo,{rte:L(function(a,b,c){var d=c[0],e=b.O();a={node:a,properties:e};if(b=b.W())b=un(b,!0,d),a.geometryLayout=b.f,e.rtept=b.Z();d=To[c[c.length-1].node.namespaceURI];e=$k(e,d);bl(a,Uo,Zk,e,c,d)}),trk:L(function(a,b,c){var d=c[0],e=b.O();a={node:a,properties:e};if(b=b.W())b=un(b,!0,d),e.trkseg=b.md();d=Vo[c[c.length-1].node.namespaceURI];e=$k(e,d);bl(a,Yo,Zk,e,c,d)}),wpt:L(function(a,b,c){var d=c[0],e=c[c.length-1];e.properties=b.O();if(b=b.W())b=un(b,!0,d),e.geometryLayout=b.f,Po(a,b.Z(), +c)})});uo.prototype.a=function(a,b){b=tn(this,b);var c=Mk("http://www.topografix.com/GPX/1/1","gpx");c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation","http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd");c.setAttribute("version","1.1");c.setAttribute("creator","OpenLayers 3");bl({node:c},ap,$o,a,[b]);return c};function bp(){this.defaultDataProjection=null}y(bp,rn);function cp(a){return"string"===typeof a?a:""}k=bp.prototype;k.X=function(){return"text"};k.Rb=function(a,b){return this.ud(cp(a),tn(this,b))};k.Fa=function(a,b){return this.Kf(cp(a),tn(this,b))};k.Vc=function(a,b){return this.wd(cp(a),tn(this,b))};k.Oa=function(a){cp(a);return this.defaultDataProjection};k.Dd=function(a,b){return this.He(a,tn(this,b))};k.Xb=function(a,b){return this.Ci(a,tn(this,b))}; +k.Zc=function(a,b){return this.Ed(a,tn(this,b))};function dp(a){a=a?a:{};this.defaultDataProjection=null;this.defaultDataProjection=yc("EPSG:4326");this.b=a.altitudeMode?a.altitudeMode:"none"}y(dp,bp);var ep=/^B(\d{2})(\d{2})(\d{2})(\d{2})(\d{5})([NS])(\d{3})(\d{5})([EW])([AV])(\d{5})(\d{5})/,fp=/^H.([A-Z]{3}).*?:(.*)/,gp=/^HFDTE(\d{2})(\d{2})(\d{2})/,hp=/\r\n|\r|\n/; +dp.prototype.ud=function(a,b){var c=this.b,d=a.split(hp),e={},f=[],g=2E3,h=0,l=1,m=-1,n,p;n=0;for(p=d.length;n<p;++n){var q=d[n],r;if("B"==q.charAt(0)){if(r=ep.exec(q)){var q=parseInt(r[1],10),u=parseInt(r[2],10),x=parseInt(r[3],10),v=parseInt(r[4],10)+parseInt(r[5],10)/6E4;"S"==r[6]&&(v=-v);var D=parseInt(r[7],10)+parseInt(r[8],10)/6E4;"W"==r[9]&&(D=-D);f.push(D,v);"none"!=c&&f.push("gps"==c?parseInt(r[11],10):"barometric"==c?parseInt(r[12],10):0);r=Date.UTC(g,h,l,q,u,x);r<m&&(r=Date.UTC(g,h,l+1, +q,u,x));f.push(r/1E3);m=r}}else"H"==q.charAt(0)&&((r=gp.exec(q))?(l=parseInt(r[1],10),h=parseInt(r[2],10)-1,g=2E3+parseInt(r[3],10)):(r=fp.exec(q))&&(e[r[1]]=r[2].trim()))}if(0===f.length)return null;d=new R(null);d.ba("none"==c?"XYM":"XYZM",f);c=new Ik(un(d,!1,b));c.G(e);return c};dp.prototype.Kf=function(a,b){var c=this.ud(a,b);return c?[c]:[]};function ip(a,b){this.a={};this.b=[];this.g=0;var c=arguments.length;if(1<c){if(c%2)throw Error("Uneven number of arguments");for(var d=0;d<c;d+=2)this.set(arguments[d],arguments[d+1])}else if(a){var e;if(a instanceof ip)e=a.N(),d=a.zc();else{var c=[],f=0;for(e in a)c[f++]=e;e=c;c=[];f=0;for(d in a)c[f++]=a[d];d=c}for(c=0;c<e.length;c++)this.set(e[c],d[c])}}k=ip.prototype;k.wc=function(){return this.g};k.zc=function(){jp(this);for(var a=[],b=0;b<this.b.length;b++)a.push(this.a[this.b[b]]);return a}; +k.N=function(){jp(this);return this.b.concat()};k.Ya=function(){return 0==this.g};k.clear=function(){this.a={};this.g=this.b.length=0};k.remove=function(a){return kp(this.a,a)?(delete this.a[a],this.g--,this.b.length>2*this.g&&jp(this),!0):!1};function jp(a){if(a.g!=a.b.length){for(var b=0,c=0;b<a.b.length;){var d=a.b[b];kp(a.a,d)&&(a.b[c++]=d);b++}a.b.length=c}if(a.g!=a.b.length){for(var e={},c=b=0;b<a.b.length;)d=a.b[b],kp(e,d)||(a.b[c++]=d,e[d]=1),b++;a.b.length=c}} +k.get=function(a,b){return kp(this.a,a)?this.a[a]:b};k.set=function(a,b){kp(this.a,a)||(this.g++,this.b.push(a));this.a[a]=b};k.forEach=function(a,b){for(var c=this.N(),d=0;d<c.length;d++){var e=c[d],f=this.get(e);a.call(b,f,e,this)}};k.clone=function(){return new ip(this)};function kp(a,b){return Object.prototype.hasOwnProperty.call(a,b)};var lp=/^(?:([^:/?#.]+):)?(?:\/\/(?:([^/?#]*)@)?([^/#?]*?)(?::([0-9]+))?(?=[/#?]|$))?([^?#]+)?(?:\?([^#]*))?(?:#(.*))?$/;function mp(a,b){if(a)for(var c=a.split("&"),d=0;d<c.length;d++){var e=c[d].indexOf("="),f,g=null;0<=e?(f=c[d].substring(0,e),g=c[d].substring(e+1)):f=c[d];b(f,g?decodeURIComponent(g.replace(/\+/g," ")):"")}};function np(a,b){this.a=this.l=this.g="";this.o=null;this.f=this.b="";this.c=!1;var c;a instanceof np?(this.c=void 0!==b?b:a.c,op(this,a.g),this.l=a.l,this.a=a.a,pp(this,a.o),this.b=a.b,qp(this,a.i.clone()),this.f=a.f):a&&(c=String(a).match(lp))?(this.c=!!b,op(this,c[1]||"",!0),this.l=rp(c[2]||""),this.a=rp(c[3]||"",!0),pp(this,c[4]),this.b=rp(c[5]||"",!0),qp(this,c[6]||"",!0),this.f=rp(c[7]||"")):(this.c=!!b,this.i=new sp(null,0,this.c))} +np.prototype.toString=function(){var a=[],b=this.g;b&&a.push(tp(b,up,!0),":");var c=this.a;if(c||"file"==b)a.push("//"),(b=this.l)&&a.push(tp(b,up,!0),"@"),a.push(encodeURIComponent(String(c)).replace(/%25([0-9a-fA-F]{2})/g,"%$1")),c=this.o,null!=c&&a.push(":",String(c));if(c=this.b)this.a&&"/"!=c.charAt(0)&&a.push("/"),a.push(tp(c,"/"==c.charAt(0)?vp:wp,!0));(c=this.i.toString())&&a.push("?",c);(c=this.f)&&a.push("#",tp(c,xp));return a.join("")};np.prototype.clone=function(){return new np(this)}; +function op(a,b,c){a.g=c?rp(b,!0):b;a.g&&(a.g=a.g.replace(/:$/,""))}function pp(a,b){if(b){b=Number(b);if(isNaN(b)||0>b)throw Error("Bad port number "+b);a.o=b}else a.o=null}function qp(a,b,c){b instanceof sp?(a.i=b,yp(a.i,a.c)):(c||(b=tp(b,zp)),a.i=new sp(b,0,a.c))}function Ap(a){return a instanceof np?a.clone():new np(a,void 0)} +function Bp(a,b){a instanceof np||(a=Ap(a));b instanceof np||(b=Ap(b));var c=a,d=b,e=c.clone(),f=!!d.g;f?op(e,d.g):f=!!d.l;f?e.l=d.l:f=!!d.a;f?e.a=d.a:f=null!=d.o;var g=d.b;if(f)pp(e,d.o);else if(f=!!d.b)if("/"!=g.charAt(0)&&(c.a&&!c.b?g="/"+g:(c=e.b.lastIndexOf("/"),-1!=c&&(g=e.b.substr(0,c+1)+g))),c=g,".."==c||"."==c)g="";else if(-1!=c.indexOf("./")||-1!=c.indexOf("/.")){for(var g=0==c.lastIndexOf("/",0),c=c.split("/"),h=[],l=0;l<c.length;){var m=c[l++];"."==m?g&&l==c.length&&h.push(""):".."==m? +((1<h.length||1==h.length&&""!=h[0])&&h.pop(),g&&l==c.length&&h.push("")):(h.push(m),g=!0)}g=h.join("/")}else g=c;f?e.b=g:f=""!==d.i.toString();f?qp(e,rp(d.i.toString())):f=!!d.f;f&&(e.f=d.f);return e}function rp(a,b){return a?b?decodeURI(a.replace(/%25/g,"%2525")):decodeURIComponent(a):""}function tp(a,b,c){return da(a)?(a=encodeURI(a).replace(b,Cp),c&&(a=a.replace(/%25([0-9a-fA-F]{2})/g,"%$1")),a):null}function Cp(a){a=a.charCodeAt(0);return"%"+(a>>4&15).toString(16)+(a&15).toString(16)} +var up=/[#\/\?@]/g,wp=/[\#\?:]/g,vp=/[\#\?]/g,zp=/[\#\?@]/g,xp=/#/g;function sp(a,b,c){this.a=this.b=null;this.g=a||null;this.f=!!c}function Dp(a){a.b||(a.b=new ip,a.a=0,a.g&&mp(a.g,function(b,c){a.add(decodeURIComponent(b.replace(/\+/g," ")),c)}))}k=sp.prototype;k.wc=function(){Dp(this);return this.a};k.add=function(a,b){Dp(this);this.g=null;a=Ep(this,a);var c=this.b.get(a);c||this.b.set(a,c=[]);c.push(b);this.a=this.a+1;return this}; +k.remove=function(a){Dp(this);a=Ep(this,a);return kp(this.b.a,a)?(this.g=null,this.a=this.a-this.b.get(a).length,this.b.remove(a)):!1};k.clear=function(){this.b=this.g=null;this.a=0};k.Ya=function(){Dp(this);return 0==this.a};function Fp(a,b){Dp(a);b=Ep(a,b);return kp(a.b.a,b)}k.N=function(){Dp(this);for(var a=this.b.zc(),b=this.b.N(),c=[],d=0;d<b.length;d++)for(var e=a[d],f=0;f<e.length;f++)c.push(b[d]);return c}; +k.zc=function(a){Dp(this);var b=[];if(da(a))Fp(this,a)&&(b=ne(b,this.b.get(Ep(this,a))));else{a=this.b.zc();for(var c=0;c<a.length;c++)b=ne(b,a[c])}return b};k.set=function(a,b){Dp(this);this.g=null;a=Ep(this,a);Fp(this,a)&&(this.a=this.a-this.b.get(a).length);this.b.set(a,[b]);this.a=this.a+1;return this};k.get=function(a,b){var c=a?this.zc(a):[];return 0<c.length?String(c[0]):b}; +k.toString=function(){if(this.g)return this.g;if(!this.b)return"";for(var a=[],b=this.b.N(),c=0;c<b.length;c++)for(var d=b[c],e=encodeURIComponent(String(d)),d=this.zc(d),f=0;f<d.length;f++){var g=e;""!==d[f]&&(g+="="+encodeURIComponent(String(d[f])));a.push(g)}return this.g=a.join("&")};k.clone=function(){var a=new sp;a.g=this.g;this.b&&(a.b=this.b.clone(),a.a=this.a);return a};function Ep(a,b){var c=String(b);a.f&&(c=c.toLowerCase());return c} +function yp(a,b){b&&!a.f&&(Dp(a),a.g=null,a.b.forEach(function(a,b){var e=b.toLowerCase();b!=e&&(this.remove(b),this.remove(e),0<a.length&&(this.g=null,this.b.set(Ep(this,e),oe(a)),this.a=this.a+a.length))},a));a.f=b};function Gp(a){a=a||{};this.g=a.font;this.i=a.rotation;this.a=a.scale;this.s=a.text;this.o=a.textAlign;this.j=a.textBaseline;this.b=void 0!==a.fill?a.fill:new ij({color:"#333"});this.l=void 0!==a.stroke?a.stroke:null;this.f=void 0!==a.offsetX?a.offsetX:0;this.c=void 0!==a.offsetY?a.offsetY:0}k=Gp.prototype;k.Xj=function(){return this.g};k.kk=function(){return this.f};k.lk=function(){return this.c};k.Un=function(){return this.b};k.Vn=function(){return this.i};k.Wn=function(){return this.a};k.Xn=function(){return this.l}; +k.Ha=function(){return this.s};k.yk=function(){return this.o};k.zk=function(){return this.j};k.bp=function(a){this.g=a};k.mi=function(a){this.f=a};k.ni=function(a){this.c=a};k.ap=function(a){this.b=a};k.Yn=function(a){this.i=a};k.Zn=function(a){this.a=a};k.ip=function(a){this.l=a};k.pi=function(a){this.s=a};k.ri=function(a){this.o=a};k.jp=function(a){this.j=a};function Hp(a){a=a?a:{};Un.call(this);this.defaultDataProjection=yc("EPSG:4326");this.g=a.defaultStyle?a.defaultStyle:Ip;this.c=void 0!==a.extractStyles?a.extractStyles:!0;this.l=void 0!==a.writeStyles?a.writeStyles:!0;this.b={};this.i=void 0!==a.showPointNames?a.showPointNames:!0}y(Hp,Un); +var Jp=["http://www.google.com/kml/ext/2.2"],Kp=[null,"http://earth.google.com/kml/2.0","http://earth.google.com/kml/2.1","http://earth.google.com/kml/2.2","http://www.opengis.net/kml/2.2"],Lp=[255,255,255,1],Mp=new ij({color:Lp}),Np=[20,2],Op=[64,64],Pp=new Dh({anchor:Np,anchorOrigin:"bottom-left",anchorXUnits:"pixels",anchorYUnits:"pixels",crossOrigin:"anonymous",rotation:0,scale:.5,size:Op,src:"https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png"}),Qp=new oj({color:Lp,width:1}),Rp=new Gp({font:"bold 16px Helvetica", +fill:Mp,stroke:new oj({color:[51,51,51,1],width:2}),scale:.8}),Ip=[new rj({fill:Mp,image:Pp,text:Rp,stroke:Qp,zIndex:0})],Sp={fraction:"fraction",pixels:"pixels"};function Tp(a,b){var c,d=[0,0],e="start";if(a.a){var f=a.a.ld();f&&2==f.length&&(d[0]=a.a.i*f[0]/2,d[1]=-a.a.i*f[1]/2,e="left")}if(Ha(a.Ha()))c=new Gp({text:b,offsetX:d[0],offsetY:d[1],textAlign:e});else{var f=a.Ha(),g={};for(c in f)g[c]=f[c];c=g;c.pi(b);c.ri(e);c.mi(d[0]);c.ni(d[1])}return new rj({text:c})} +function Up(a,b,c,d,e){return function(){var f=e,g="";f&&this.W()&&(f="Point"===this.W().X());f&&(g=this.get("name"),f=f&&g);if(a)return f?(f=Tp(a[0],g),a.concat(f)):a;if(b){var h=Vp(b,c,d);return f?(f=Tp(h[0],g),h.concat(f)):h}return f?(f=Tp(c[0],g),c.concat(f)):c}}function Vp(a,b,c){return Array.isArray(a)?a:"string"===typeof a?(!(a in c)&&"#"+a in c&&(a="#"+a),Vp(c[a],b,c)):b} +function Wp(a){a=Nk(a,!1);if(a=/^\s*#?\s*([0-9A-Fa-f]{8})\s*$/.exec(a))return a=a[1],[parseInt(a.substr(6,2),16),parseInt(a.substr(4,2),16),parseInt(a.substr(2,2),16),parseInt(a.substr(0,2),16)/255]}function Xp(a){a=Nk(a,!1);for(var b=[],c=/^\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?)(?:\s*,\s*([+\-]?\d*\.?\d+(?:e[+\-]?\d+)?))?\s*/i,d;d=c.exec(a);)b.push(parseFloat(d[1]),parseFloat(d[2]),d[3]?parseFloat(d[3]):0),a=a.substr(d[0].length);return""!==a?void 0:b} +function Yp(a){var b=Nk(a,!1);return a.baseURI?Bp(a.baseURI,b.trim()).toString():b.trim()}function Zp(a){a=bo(a);if(void 0!==a)return Math.sqrt(a)}function $p(a,b){return O(null,aq,a,b)}function bq(a,b){var c=O({B:[],zi:[]},cq,a,b);if(c){var d=c.B,c=c.zi,e,f;e=0;for(f=Math.min(d.length,c.length);e<f;++e)d[4*e+3]=c[e];c=new R(null);c.ba("XYZM",d);return c}}function dq(a,b){var c=O({},eq,a,b),d=O(null,fq,a,b);if(d){var e=new R(null);e.ba("XYZ",d);e.G(c);return e}} +function gq(a,b){var c=O({},eq,a,b),d=O(null,fq,a,b);if(d){var e=new E(null);e.ba("XYZ",d,[d.length]);e.G(c);return e}} +function hq(a,b){var c=O([],iq,a,b);if(!c)return null;if(0===c.length)return new Ln(c);var d=!0,e=c[0].X(),f,g,h;g=1;for(h=c.length;g<h;++g)if(f=c[g],f.X()!=e){d=!1;break}if(d){if("Point"==e){f=c[0];d=f.f;e=f.la();g=1;for(h=c.length;g<h;++g)f=c[g],mb(e,f.la());f=new Bn(null);f.ba(d,e);jq(f,c);return f}return"LineString"==e?(f=new S(null),An(f,c),jq(f,c),f):"Polygon"==e?(f=new T(null),Dn(f,c),jq(f,c),f):"GeometryCollection"==e?new Ln(c):null}return new Ln(c)} +function kq(a,b){var c=O({},eq,a,b),d=O(null,fq,a,b);if(d){var e=new C(null);e.ba("XYZ",d);e.G(c);return e}}function lq(a,b){var c=O({},eq,a,b),d=O([null],mq,a,b);if(d&&d[0]){var e=new E(null),f=d[0],g=[f.length],h,l;h=1;for(l=d.length;h<l;++h)mb(f,d[h]),g.push(f.length);e.ba("XYZ",f,g);e.G(c);return e}} +function nq(a,b){var c=O({},oq,a,b);if(!c)return null;var d="fillStyle"in c?c.fillStyle:Mp,e=c.fill;void 0===e||e||(d=null);var e="imageStyle"in c?c.imageStyle:Pp,f="textStyle"in c?c.textStyle:Rp,g="strokeStyle"in c?c.strokeStyle:Qp,c=c.outline;void 0===c||c||(g=null);return[new rj({fill:d,image:e,stroke:g,text:f,zIndex:void 0})]} +function jq(a,b){var c=b.length,d=Array(b.length),e=Array(b.length),f,g,h,l;h=l=!1;for(g=0;g<c;++g)f=b[g],d[g]=f.get("extrude"),e[g]=f.get("altitudeMode"),h=h||void 0!==d[g],l=l||e[g];h&&a.set("extrude",d);l&&a.set("altitudeMode",e)}function pq(a,b){al(qq,a,b)} +var rq=M(Kp,{value:Uk(U)}),qq=M(Kp,{Data:function(a,b){var c=a.getAttribute("name");if(null!==c){var d=O(void 0,rq,a,b);d&&(b[b.length-1][c]=d)}},SchemaData:function(a,b){al(sq,a,b)}}),eq=M(Kp,{extrude:J(Zn),altitudeMode:J(U)}),aq=M(Kp,{coordinates:Uk(Xp)}),mq=M(Kp,{innerBoundaryIs:function(a,b){var c=O(void 0,tq,a,b);c&&b[b.length-1].push(c)},outerBoundaryIs:function(a,b){var c=O(void 0,uq,a,b);c&&(b[b.length-1][0]=c)}}),cq=M(Kp,{when:function(a,b){var c=b[b.length-1].zi,d=Nk(a,!1);if(d=/^\s*(\d{4})($|-(\d{2})($|-(\d{2})($|T(\d{2}):(\d{2}):(\d{2})(Z|(?:([+\-])(\d{2})(?::(\d{2}))?)))))\s*$/.exec(d)){var e= +Date.UTC(parseInt(d[1],10),d[3]?parseInt(d[3],10)-1:0,d[5]?parseInt(d[5],10):1,d[7]?parseInt(d[7],10):0,d[8]?parseInt(d[8],10):0,d[9]?parseInt(d[9],10):0);if(d[10]&&"Z"!=d[10]){var f="-"==d[11]?-1:1,e=e+60*f*parseInt(d[12],10);d[13]&&(e+=3600*f*parseInt(d[13],10))}c.push(e)}else c.push(0)}},M(Jp,{coord:function(a,b){var c=b[b.length-1].B,d=Nk(a,!1);(d=/^\s*([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s+([+\-]?\d+(?:\.\d*)?(?:e[+\-]?\d*)?)\s*$/i.exec(d))?c.push(parseFloat(d[1]), +parseFloat(d[2]),parseFloat(d[3]),0):c.push(0,0,0,0)}})),fq=M(Kp,{coordinates:Uk(Xp)}),vq=M(Kp,{href:J(Yp)},M(Jp,{x:J(bo),y:J(bo),w:J(bo),h:J(bo)})),wq=M(Kp,{Icon:J(function(a,b){var c=O({},vq,a,b);return c?c:null}),heading:J(bo),hotSpot:J(function(a){var b=a.getAttribute("xunits"),c=a.getAttribute("yunits");return{x:parseFloat(a.getAttribute("x")),$f:Sp[b],y:parseFloat(a.getAttribute("y")),ag:Sp[c]}}),scale:J(Zp)}),tq=M(Kp,{LinearRing:Uk($p)}),xq=M(Kp,{color:J(Wp),scale:J(Zp)}),yq=M(Kp,{color:J(Wp), +width:J(bo)}),iq=M(Kp,{LineString:Tk(dq),LinearRing:Tk(gq),MultiGeometry:Tk(hq),Point:Tk(kq),Polygon:Tk(lq)}),zq=M(Jp,{Track:Tk(bq)}),Bq=M(Kp,{ExtendedData:pq,Link:function(a,b){al(Aq,a,b)},address:J(U),description:J(U),name:J(U),open:J(Zn),phoneNumber:J(U),visibility:J(Zn)}),Aq=M(Kp,{href:J(Yp)}),uq=M(Kp,{LinearRing:Uk($p)}),Cq=M(Kp,{Style:J(nq),key:J(U),styleUrl:J(function(a){var b=Nk(a,!1).trim();return a.baseURI?Bp(a.baseURI,b).toString():b})}),Eq=M(Kp,{ExtendedData:pq,MultiGeometry:J(hq,"geometry"), +LineString:J(dq,"geometry"),LinearRing:J(gq,"geometry"),Point:J(kq,"geometry"),Polygon:J(lq,"geometry"),Style:J(nq),StyleMap:function(a,b){var c=O(void 0,Dq,a,b);if(c){var d=b[b.length-1];Array.isArray(c)?d.Style=c:"string"===typeof c&&(d.styleUrl=c)}},address:J(U),description:J(U),name:J(U),open:J(Zn),phoneNumber:J(U),styleUrl:J(Yp),visibility:J(Zn)},M(Jp,{MultiTrack:J(function(a,b){var c=O([],zq,a,b);if(c){var d=new S(null);An(d,c);return d}},"geometry"),Track:J(bq,"geometry")})),Fq=M(Kp,{color:J(Wp), +fill:J(Zn),outline:J(Zn)}),sq=M(Kp,{SimpleData:function(a,b){var c=a.getAttribute("name");if(null!==c){var d=U(a);b[b.length-1][c]=d}}}),oq=M(Kp,{IconStyle:function(a,b){var c=O({},wq,a,b);if(c){var d=b[b.length-1],e="Icon"in c?c.Icon:{},f;f=(f=e.href)?f:"https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png";var g,h,l,m=c.hotSpot;m?(g=[m.x,m.y],h=m.$f,l=m.ag):"https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png"===f?(g=Np,l=h="pixels"):/^http:\/\/maps\.(?:google|gstatic)\.com\//.test(f)&& +(g=[.5,0],l=h="fraction");var n,m=e.x,p=e.y;void 0!==m&&void 0!==p&&(n=[m,p]);var q,m=e.w,e=e.h;void 0!==m&&void 0!==e&&(q=[m,e]);var r,e=c.heading;void 0!==e&&(r=wa(e));c=c.scale;"https://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png"==f&&(q=Op,void 0===c&&(c=.5));g=new Dh({anchor:g,anchorOrigin:"bottom-left",anchorXUnits:h,anchorYUnits:l,crossOrigin:"anonymous",offset:n,offsetOrigin:"bottom-left",rotation:r,scale:c,size:q,src:f});d.imageStyle=g}},LabelStyle:function(a,b){var c=O({},xq,a, +b);c&&(b[b.length-1].textStyle=new Gp({fill:new ij({color:"color"in c?c.color:Lp}),scale:c.scale}))},LineStyle:function(a,b){var c=O({},yq,a,b);c&&(b[b.length-1].strokeStyle=new oj({color:"color"in c?c.color:Lp,width:"width"in c?c.width:1}))},PolyStyle:function(a,b){var c=O({},Fq,a,b);if(c){var d=b[b.length-1];d.fillStyle=new ij({color:"color"in c?c.color:Lp});var e=c.fill;void 0!==e&&(d.fill=e);c=c.outline;void 0!==c&&(d.outline=c)}}}),Dq=M(Kp,{Pair:function(a,b){var c=O({},Cq,a,b);if(c){var d=c.key; +d&&"normal"==d&&((d=c.styleUrl)&&(b[b.length-1]=d),(c=c.Style)&&(b[b.length-1]=c))}}});k=Hp.prototype;k.Gf=function(a,b){var c=M(Kp,{Document:Sk(this.Gf,this),Folder:Sk(this.Gf,this),Placemark:Tk(this.Of,this),Style:this.Lo.bind(this),StyleMap:this.Ko.bind(this)});if(c=O([],c,a,b,this))return c}; +k.Of=function(a,b){var c=O({geometry:null},Eq,a,b);if(c){var d=new Ik,e=a.getAttribute("id");null!==e&&d.mc(e);var e=b[0],f=c.geometry;f&&un(f,!1,e);d.Ua(f);delete c.geometry;this.c&&d.sf(Up(c.Style,c.styleUrl,this.g,this.b,this.i));delete c.Style;d.G(c);return d}};k.Lo=function(a,b){var c=a.getAttribute("id");if(null!==c){var d=nq(a,b);d&&(c=a.baseURI?Bp(a.baseURI,"#"+c).toString():"#"+c,this.b[c]=d)}}; +k.Ko=function(a,b){var c=a.getAttribute("id");if(null!==c){var d=O(void 0,Dq,a,b);d&&(c=a.baseURI?Bp(a.baseURI,"#"+c).toString():"#"+c,this.b[c]=d)}};k.Ph=function(a,b){if(!jb(Kp,a.namespaceURI))return null;var c=this.Of(a,[sn(this,a,b)]);return c?c:null}; +k.lc=function(a,b){if(!jb(Kp,a.namespaceURI))return[];var c;c=a.localName;if("Document"==c||"Folder"==c)return(c=this.Gf(a,[sn(this,a,b)]))?c:[];if("Placemark"==c)return(c=this.Of(a,[sn(this,a,b)]))?[c]:[];if("kml"==c){c=[];var d;for(d=a.firstElementChild;d;d=d.nextElementSibling){var e=this.lc(d,b);e&&mb(c,e)}return c}return[]};k.Fo=function(a){if(Pk(a))return Gq(this,a);if(Qk(a))return Hq(this,a);if("string"===typeof a)return a=Rk(a),Gq(this,a)}; +function Gq(a,b){var c;for(c=b.firstChild;c;c=c.nextSibling)if(c.nodeType==Node.ELEMENT_NODE){var d=Hq(a,c);if(d)return d}}function Hq(a,b){var c;for(c=b.firstElementChild;c;c=c.nextElementSibling)if(jb(Kp,c.namespaceURI)&&"name"==c.localName)return U(c);for(c=b.firstElementChild;c;c=c.nextElementSibling){var d=c.localName;if(jb(Kp,c.namespaceURI)&&("Document"==d||"Folder"==d||"Placemark"==d||"kml"==d)&&(d=Hq(a,c)))return d}} +k.Go=function(a){var b=[];Pk(a)?mb(b,Iq(this,a)):Qk(a)?mb(b,Jq(this,a)):"string"===typeof a&&(a=Rk(a),mb(b,Iq(this,a)));return b};function Iq(a,b){var c,d=[];for(c=b.firstChild;c;c=c.nextSibling)c.nodeType==Node.ELEMENT_NODE&&mb(d,Jq(a,c));return d} +function Jq(a,b){var c,d=[];for(c=b.firstElementChild;c;c=c.nextElementSibling)if(jb(Kp,c.namespaceURI)&&"NetworkLink"==c.localName){var e=O({},Bq,c,[]);d.push(e)}for(c=b.firstElementChild;c;c=c.nextElementSibling)e=c.localName,!jb(Kp,c.namespaceURI)||"Document"!=e&&"Folder"!=e&&"kml"!=e||mb(d,Jq(a,c));return d}function Kq(a,b){var c=te(b),c=[255*(4==c.length?c[3]:1),c[2],c[1],c[0]],d;for(d=0;4>d;++d){var e=parseInt(c[d],10).toString(16);c[d]=1==e.length?"0"+e:e}ho(a,c.join(""))} +function Lq(a,b,c){a={node:a};var d=b.X(),e,f;"GeometryCollection"==d?(e=b.ff(),f=Mq):"MultiPoint"==d?(e=b.je(),f=Nq):"MultiLineString"==d?(e=b.md(),f=Oq):"MultiPolygon"==d&&(e=b.Wd(),f=Pq);bl(a,Qq,f,e,c)}function Rq(a,b,c){bl({node:a},Sq,Tq,[b],c)} +function Uq(a,b,c){var d={node:a};b.Xa()&&a.setAttribute("id",b.Xa());a=b.O();var e=b.ec();e&&(e=e.call(b,0))&&(e=Array.isArray(e)?e[0]:e,this.l&&(a.Style=e),(e=e.Ha())&&(a.name=e.Ha()));e=Vq[c[c.length-1].node.namespaceURI];a=$k(a,e);bl(d,Wq,Zk,a,c,e);a=c[0];(b=b.W())&&(b=un(b,!0,a));bl(d,Wq,Mq,[b],c)}function Xq(a,b,c){var d=b.la();a={node:a};a.layout=b.f;a.stride=b.va();bl(a,Yq,Zq,[d],c)}function $q(a,b,c){b=b.Vd();var d=b.shift();a={node:a};bl(a,ar,br,b,c);bl(a,ar,cr,[d],c)} +function dr(a,b){io(a,Math.round(b*b*1E6)/1E6)} +var er=M(Kp,["Document","Placemark"]),hr=M(Kp,{Document:L(function(a,b,c){bl({node:a},fr,gr,b,c,void 0,this)}),Placemark:L(Uq)}),fr=M(Kp,{Placemark:L(Uq)}),ir={Point:"Point",LineString:"LineString",LinearRing:"LinearRing",Polygon:"Polygon",MultiPoint:"MultiGeometry",MultiLineString:"MultiGeometry",MultiPolygon:"MultiGeometry",GeometryCollection:"MultiGeometry"},jr=M(Kp,["href"],M(Jp,["x","y","w","h"])),kr=M(Kp,{href:L(ho)},M(Jp,{x:L(io),y:L(io),w:L(io),h:L(io)})),lr=M(Kp,["scale","heading","Icon", +"hotSpot"]),nr=M(Kp,{Icon:L(function(a,b,c){a={node:a};var d=jr[c[c.length-1].node.namespaceURI],e=$k(b,d);bl(a,kr,Zk,e,c,d);d=jr[Jp[0]];e=$k(b,d);bl(a,kr,mr,e,c,d)}),heading:L(io),hotSpot:L(function(a,b){a.setAttribute("x",b.x);a.setAttribute("y",b.y);a.setAttribute("xunits",b.$f);a.setAttribute("yunits",b.ag)}),scale:L(dr)}),or=M(Kp,["color","scale"]),pr=M(Kp,{color:L(Kq),scale:L(dr)}),qr=M(Kp,["color","width"]),rr=M(Kp,{color:L(Kq),width:L(io)}),Sq=M(Kp,{LinearRing:L(Xq)}),Qq=M(Kp,{LineString:L(Xq), +Point:L(Xq),Polygon:L($q),GeometryCollection:L(Lq)}),Vq=M(Kp,"name open visibility address phoneNumber description styleUrl Style".split(" ")),Wq=M(Kp,{MultiGeometry:L(Lq),LineString:L(Xq),LinearRing:L(Xq),Point:L(Xq),Polygon:L($q),Style:L(function(a,b,c){a={node:a};var d={},e=b.c,f=b.f,g=b.a;b=b.Ha();g instanceof Dh&&(d.IconStyle=g);b&&(d.LabelStyle=b);f&&(d.LineStyle=f);e&&(d.PolyStyle=e);b=sr[c[c.length-1].node.namespaceURI];d=$k(d,b);bl(a,tr,Zk,d,c,b)}),address:L(ho),description:L(ho),name:L(ho), +open:L(go),phoneNumber:L(ho),styleUrl:L(ho),visibility:L(go)}),Yq=M(Kp,{coordinates:L(function(a,b,c){c=c[c.length-1];var d=c.layout;c=c.stride;var e;"XY"==d||"XYM"==d?e=2:("XYZ"==d||"XYZM"==d)&&(e=3);var f,g=b.length,h="";if(0<g){h+=b[0];for(d=1;d<e;++d)h+=","+b[d];for(f=c;f<g;f+=c)for(h+=" "+b[f],d=1;d<e;++d)h+=","+b[f+d]}ho(a,h)})}),ar=M(Kp,{outerBoundaryIs:L(Rq),innerBoundaryIs:L(Rq)}),ur=M(Kp,{color:L(Kq)}),sr=M(Kp,["IconStyle","LabelStyle","LineStyle","PolyStyle"]),tr=M(Kp,{IconStyle:L(function(a, +b,c){a={node:a};var d={},e=b.Fb(),f=b.ld(),g={href:b.b.j};if(e){g.w=e[0];g.h=e[1];var h=b.Yb(),l=b.Ia();l&&f&&0!==l[0]&&l[1]!==e[1]&&(g.x=l[0],g.y=f[1]-(l[1]+e[1]));h&&0!==h[0]&&h[1]!==e[1]&&(d.hotSpot={x:h[0],$f:"pixels",y:e[1]-h[1],ag:"pixels"})}d.Icon=g;e=b.i;1!==e&&(d.scale=e);b=b.j;0!==b&&(d.heading=b);b=lr[c[c.length-1].node.namespaceURI];d=$k(d,b);bl(a,nr,Zk,d,c,b)}),LabelStyle:L(function(a,b,c){a={node:a};var d={},e=b.b;e&&(d.color=e.b);(b=b.a)&&1!==b&&(d.scale=b);b=or[c[c.length-1].node.namespaceURI]; +d=$k(d,b);bl(a,pr,Zk,d,c,b)}),LineStyle:L(function(a,b,c){a={node:a};var d=qr[c[c.length-1].node.namespaceURI];b=$k({color:b.b,width:b.a},d);bl(a,rr,Zk,b,c,d)}),PolyStyle:L(function(a,b,c){bl({node:a},ur,vr,[b.b],c)})});function mr(a,b,c){return Mk(Jp[0],"gx:"+c)}function gr(a,b){return Mk(b[b.length-1].node.namespaceURI,"Placemark")}function Mq(a,b){if(a)return Mk(b[b.length-1].node.namespaceURI,ir[a.X()])} +var vr=Xk("color"),Zq=Xk("coordinates"),br=Xk("innerBoundaryIs"),Nq=Xk("Point"),Oq=Xk("LineString"),Tq=Xk("LinearRing"),Pq=Xk("Polygon"),cr=Xk("outerBoundaryIs"); +Hp.prototype.a=function(a,b){b=tn(this,b);var c=Mk(Kp[4],"kml");c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:gx",Jp[0]);c.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xsi","http://www.w3.org/2001/XMLSchema-instance");c.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation","http://www.opengis.net/kml/2.2 https://developers.google.com/kml/schema/kml22gx.xsd");var d={node:c},e={};1<a.length?e.Document=a:1==a.length&&(e.Placemark=a[0]);var f=er[c.namespaceURI], +e=$k(e,f);bl(d,hr,Zk,e,[b],f,this);return c};(function(){var a={},b={ja:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ja=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Rp=c()}})(function(){return function d(a,b,g){function h(m,p){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!p&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ja:{}};a[m][0].call(q.ja,function(b){var d= +a[m][1][b];return h(d?d:b)},q,q.ja,d,a,b,g)}return b[m].ja}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b,f){f.read=function(a,b,d,e,f){var p;p=8*f-e-1;var q=(1<<p)-1,r=q>>1,u=-7;f=d?f-1:0;var x=d?-1:1,v=a[b+f];f+=x;d=v&(1<<-u)-1;v>>=-u;for(u+=p;0<u;d=256*d+a[b+f],f+=x,u-=8);p=d&(1<<-u)-1;d>>=-u;for(u+=e;0<u;p=256*p+a[b+f],f+=x,u-=8);if(0===d)d=1-r;else{if(d===q)return p?NaN:Infinity*(v?-1:1);p+=Math.pow(2,e);d-=r}return(v?-1:1)*p*Math.pow(2,d- +e)};f.write=function(a,b,d,e,f,p){var q,r=8*p-f-1,u=(1<<r)-1,x=u>>1,v=23===f?Math.pow(2,-24)-Math.pow(2,-77):0;p=e?0:p-1;var D=e?1:-1,A=0>b||0===b&&0>1/b?1:0;b=Math.abs(b);isNaN(b)||Infinity===b?(b=isNaN(b)?1:0,e=u):(e=Math.floor(Math.log(b)/Math.LN2),1>b*(q=Math.pow(2,-e))&&(e--,q*=2),b=1<=e+x?b+v/q:b+v*Math.pow(2,1-x),2<=b*q&&(e++,q/=2),e+x>=u?(b=0,e=u):1<=e+x?(b=(b*q-1)*Math.pow(2,f),e+=x):(b=b*Math.pow(2,x-1)*Math.pow(2,f),e=0));for(;8<=f;a[d+p]=b&255,p+=D,b/=256,f-=8);e=e<<f|b;for(r+=f;0<r;a[d+ +p]=e&255,p+=D,e/=256,r-=8);a[d+p-D]|=128*A}},{}],2:[function(a,b){function f(a){var b;a&&a.length&&(b=a,a=b.length);a=new Uint8Array(a||0);b&&a.set(b);a.$h=l.$h;a.Zf=l.Zf;a.Sh=l.Sh;a.Ei=l.Ei;a.Nf=l.Nf;a.Di=l.Di;a.Hf=l.Hf;a.Ai=l.Ai;a.toString=l.toString;a.write=l.write;a.slice=l.slice;a.tg=l.tg;a.nj=!0;return a}function g(a){for(var b=a.length,d=[],e=0,f,g;e<b;e++){f=a.charCodeAt(e);if(55295<f&&57344>f)if(g)if(56320>f){d.push(239,191,189);g=f;continue}else f=g-55296<<10|f-56320|65536,g=null;else{56319< +f||e+1===b?d.push(239,191,189):g=f;continue}else g&&(d.push(239,191,189),g=null);128>f?d.push(f):2048>f?d.push(f>>6|192,f&63|128):65536>f?d.push(f>>12|224,f>>6&63|128,f&63|128):d.push(f>>18|240,f>>12&63|128,f>>6&63|128,f&63|128)}return d}b.ja=f;var h=a("ieee754"),l,m,n;l={$h:function(a){return(this[a]|this[a+1]<<8|this[a+2]<<16)+16777216*this[a+3]},Zf:function(a,b){this[b]=a;this[b+1]=a>>>8;this[b+2]=a>>>16;this[b+3]=a>>>24},Sh:function(a){return(this[a]|this[a+1]<<8|this[a+2]<<16)+(this[a+3]<<24)}, +Nf:function(a){return h.read(this,a,!0,23,4)},Hf:function(a){return h.read(this,a,!0,52,8)},Di:function(a,b){return h.write(this,a,b,!0,23,4)},Ai:function(a,b){return h.write(this,a,b,!0,52,8)},toString:function(a,b,d){var e=a="";d=Math.min(this.length,d||this.length);for(b=b||0;b<d;b++){var f=this[b];127>=f?(a+=decodeURIComponent(e)+String.fromCharCode(f),e=""):e+="%"+f.toString(16)}return a+=decodeURIComponent(e)},write:function(a,b){for(var d=a===m?n:g(a),e=0;e<d.length;e++)this[b+e]=d[e]},slice:function(a, +b){return this.subarray(a,b)},tg:function(a,b){b=b||0;for(var d=0;d<this.length;d++)a[b+d]=this[d]}};l.Ei=l.Zf;f.byteLength=function(a){m=a;n=g(a);return n.length};f.isBuffer=function(a){return!(!a||!a.nj)}},{ieee754:1}],3:[function(a,b){(function(f){function g(a){this.Cb=l.isBuffer(a)?a:new l(a||0);this.da=0;this.length=this.Cb.length}function h(a,b){var d=b.Cb,e;e=d[b.da++];a+=268435456*(e&127);if(128>e)return a;e=d[b.da++];a+=34359738368*(e&127);if(128>e)return a;e=d[b.da++];a+=4398046511104*(e& +127);if(128>e)return a;e=d[b.da++];a+=562949953421312*(e&127);if(128>e)return a;e=d[b.da++];a+=72057594037927936*(e&127);if(128>e)return a;e=d[b.da++];if(128>e)return a+0x7fffffffffffffff*(e&127);throw Error("Expected varint not more than 10 bytes");}b.ja=g;var l=f.Ap||a("./buffer");g.f=0;g.g=1;g.b=2;g.a=5;var m=Math.pow(2,63);g.prototype={Lf:function(a,b,d){for(d=d||this.length;this.da<d;){var e=this.Da(),f=this.da;a(e>>3,b,this);this.da===f&&this.op(e)}return b},Bo:function(){var a=this.Cb.Nf(this.da); +this.da+=4;return a},xo:function(){var a=this.Cb.Hf(this.da);this.da+=8;return a},Da:function(){var a=this.Cb,b,d;d=a[this.da++];b=d&127;if(128>d)return b;d=a[this.da++];b|=(d&127)<<7;if(128>d)return b;d=a[this.da++];b|=(d&127)<<14;if(128>d)return b;d=a[this.da++];b|=(d&127)<<21;return 128>d?b:h(b,this)},Mo:function(){var a=this.da,b=this.Da();if(b<m)return b;for(var d=this.da-2;255===this.Cb[d];)d--;d<a&&(d=a);for(var e=b=0;e<d-a+1;e++)var f=~this.Cb[a+e]&127,b=b+(4>e?f<<7*e:f*Math.pow(2,7*e));return-b- +1},xd:function(){var a=this.Da();return 1===a%2?(a+1)/-2:a/2},vo:function(){return!!this.Da()},Qf:function(){var a=this.Da()+this.da,b=this.Cb.toString("utf8",this.da,a);this.da=a;return b},op:function(a){a&=7;if(a===g.f)for(;127<this.Cb[this.da++];);else if(a===g.b)this.da=this.Da()+this.da;else if(a===g.a)this.da+=4;else if(a===g.g)this.da+=8;else throw Error("Unimplemented type: "+a);}}}).call(this,"undefined"!==typeof global?global:"undefined"!==typeof self?self:"undefined"!==typeof window?window: +{})},{"./buffer":2}]},{},[3])(3)});hl=b.ja})();(function(){var a={},b={ja:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ja=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Up=c()}})(function(){return function d(a,b,g){function h(m,p){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!p&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ja:{}};a[m][0].call(q.ja,function(b){var d= +a[m][1][b];return h(d?d:b)},q,q.ja,d,a,b,g)}return b[m].ja}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b){function f(a,b){this.x=a;this.y=b}b.ja=f;f.prototype={clone:function(){return new f(this.x,this.y)},add:function(a){return this.clone().fj(a)},rotate:function(a){return this.clone().qj(a)},round:function(){return this.clone().rj()},angle:function(){return Math.atan2(this.y,this.x)},fj:function(a){this.x+=a.x;this.y+=a.y;return this},qj:function(a){var b= +Math.cos(a);a=Math.sin(a);var d=a*this.x+b*this.y;this.x=b*this.x-a*this.y;this.y=d;return this},rj:function(){this.x=Math.round(this.x);this.y=Math.round(this.y);return this}};f.b=function(a){return a instanceof f?a:Array.isArray(a)?new f(a[0],a[1]):a}},{}],2:[function(a,b){b.ja.ej=a("./lib/vectortile.js");b.ja.Np=a("./lib/vectortilefeature.js");b.ja.Op=a("./lib/vectortilelayer.js")},{"./lib/vectortile.js":3,"./lib/vectortilefeature.js":4,"./lib/vectortilelayer.js":5}],3:[function(a,b){function f(a, +b,d){3===a&&(a=new g(d,d.Da()+d.da),a.length&&(b[a.name]=a))}var g=a("./vectortilelayer");b.ja=function(a,b){this.layers=a.Lf(f,{},b)}},{"./vectortilelayer":5}],4:[function(a,b){function f(a,b,d,e,f){this.properties={};this.extent=d;this.type=0;this.qc=a;this.Qe=-1;this.Id=e;this.Kd=f;a.Lf(g,this,b)}function g(a,b,d){if(1==a)b.Qp=d.Da();else if(2==a)for(a=d.Da()+d.da;d.da<a;){var e=b.Id[d.Da()],f=b.Kd[d.Da()];b.properties[e]=f}else 3==a?b.type=d.Da():4==a&&(b.Qe=d.da)}var h=a("point-geometry");b.ja= +f;f.b=["Unknown","Point","LineString","Polygon"];f.prototype.Vg=function(){var a=this.qc;a.da=this.Qe;for(var b=a.Da()+a.da,d=1,e=0,f=0,g=0,u=[],x;a.da<b;)if(e||(e=a.Da(),d=e&7,e>>=3),e--,1===d||2===d)f+=a.xd(),g+=a.xd(),1===d&&(x&&u.push(x),x=[]),x.push(new h(f,g));else if(7===d)x&&x.push(x[0].clone());else throw Error("unknown command "+d);x&&u.push(x);return u};f.prototype.bbox=function(){var a=this.qc;a.da=this.Qe;for(var b=a.Da()+a.da,d=1,e=0,f=0,g=0,h=Infinity,x=-Infinity,v=Infinity,D=-Infinity;a.da< +b;)if(e||(e=a.Da(),d=e&7,e>>=3),e--,1===d||2===d)f+=a.xd(),g+=a.xd(),f<h&&(h=f),f>x&&(x=f),g<v&&(v=g),g>D&&(D=g);else if(7!==d)throw Error("unknown command "+d);return[h,v,x,D]}},{"point-geometry":1}],5:[function(a,b){function f(a,b){this.version=1;this.name=null;this.extent=4096;this.length=0;this.qc=a;this.Id=[];this.Kd=[];this.Hd=[];a.Lf(g,this,b);this.length=this.Hd.length}function g(a,b,d){15===a?b.version=d.Da():1===a?b.name=d.Qf():5===a?b.extent=d.Da():2===a?b.Hd.push(d.da):3===a?b.Id.push(d.Qf()): +4===a&&b.Kd.push(h(d))}function h(a){for(var b=null,d=a.Da()+a.da;a.da<d;)b=a.Da()>>3,b=1===b?a.Qf():2===b?a.Bo():3===b?a.xo():4===b?a.Mo():5===b?a.Da():6===b?a.xd():7===b?a.vo():null;return b}var l=a("./vectortilefeature.js");b.ja=f;f.prototype.feature=function(a){if(0>a||a>=this.Hd.length)throw Error("feature index out of bounds");this.qc.da=this.Hd[a];a=this.qc.Da()+this.qc.da;return new l(this.qc,a,this.extent,this.Id,this.Kd)}},{"./vectortilefeature.js":4}]},{},[2])(2)});il=b.ja})();function wr(a){this.defaultDataProjection=null;a=a?a:{};this.defaultDataProjection=new vc({code:"",units:"tile-pixels"});this.b=a.featureClass?a.featureClass:hk;this.g=a.geometryName?a.geometryName:"geometry";this.a=a.layerName?a.layerName:"layer";this.f=a.layers?a.layers:null}y(wr,rn);wr.prototype.X=function(){return"arraybuffer"}; +wr.prototype.Fa=function(a,b){var c=this.f,d=new hl(a),d=new il.ej(d),e=[],f=this.b,g,h,l;for(l in d.layers)if(!c||-1!=c.indexOf(l)){g=d.layers[l];for(var m=0,n=g.length;m<n;++m){if(f===hk){var p=g.feature(m);h=l;var q=p.Vg(),r=[],u=[];xr(q,u,r);var x=p.type,v=void 0;1===x?v=1===q.length?"Point":"MultiPoint":2===x?v=1===q.length?"LineString":"MultiLineString":3===x&&(v="Polygon");p=p.properties;p[this.a]=h;h=new this.b(v,u,r,p)}else{p=g.feature(m);v=l;u=b;h=new this.b;r=p.properties;r[this.a]=v;v= +p.type;if(0===v)v=null;else{p=p.Vg();q=[];x=[];xr(p,x,q);var D=void 0;1===v?D=1===p.length?new C(null):new Bn(null):2===v?1===p.length?D=new R(null):D=new S(null):3===v&&(D=new E(null));D.ba("XY",x,q);v=D}(u=un(v,!1,tn(this,u)))&&(r[this.g]=u);h.G(r);h.Ec(this.g)}e.push(h)}}return e};wr.prototype.Oa=function(){return this.defaultDataProjection};wr.prototype.c=function(a){this.f=a}; +function xr(a,b,c){for(var d=0,e=0,f=a.length;e<f;++e){var g=a[e],h,l;h=0;for(l=g.length;h<l;++h){var m=g[h];b.push(m.x,m.y)}d+=2*h;c.push(d)}};function yr(a,b){return new zr(a,b)}function Ar(a,b,c){return new Br(a,b,c)}function Cr(a){this.Wb=a}function Dr(a){this.Wb=a}y(Dr,Cr);function Er(a,b,c){this.Wb=a;this.b=b;this.a=c}y(Er,Dr);function zr(a,b){Er.call(this,"And",a,b)}y(zr,Er);function Fr(a,b){Er.call(this,"Or",a,b)}y(Fr,Er);function Gr(a){this.Wb="Not";this.condition=a}y(Gr,Dr);function Br(a,b,c){this.Wb="BBOX";this.geometryName=a;this.extent=b;this.srsName=c}y(Br,Cr);function Hr(a,b){this.Wb=a;this.b=b}y(Hr,Cr); +function Ir(a,b,c,d){Hr.call(this,a,b);this.g=c;this.a=d}y(Ir,Hr);function Jr(a,b,c){Ir.call(this,"PropertyIsEqualTo",a,b,c)}y(Jr,Ir);function Kr(a,b,c){Ir.call(this,"PropertyIsNotEqualTo",a,b,c)}y(Kr,Ir);function Lr(a,b){Ir.call(this,"PropertyIsLessThan",a,b)}y(Lr,Ir);function Mr(a,b){Ir.call(this,"PropertyIsLessThanOrEqualTo",a,b)}y(Mr,Ir);function Nr(a,b){Ir.call(this,"PropertyIsGreaterThan",a,b)}y(Nr,Ir);function Or(a,b){Ir.call(this,"PropertyIsGreaterThanOrEqualTo",a,b)}y(Or,Ir); +function Pr(a){Hr.call(this,"PropertyIsNull",a)}y(Pr,Hr);function Qr(a,b,c){Hr.call(this,"PropertyIsBetween",a);this.a=b;this.g=c}y(Qr,Hr);function Rr(a,b,c,d,e,f){Hr.call(this,"PropertyIsLike",a);this.f=b;this.i=void 0!==c?c:"*";this.c=void 0!==d?d:".";this.g=void 0!==e?e:"!";this.a=f}y(Rr,Hr);function Sr(){Un.call(this);this.defaultDataProjection=yc("EPSG:4326")}y(Sr,Un);function Tr(a,b){b[b.length-1].Cd[a.getAttribute("k")]=a.getAttribute("v")} +var Ur=[null],Vr=M(Ur,{nd:function(a,b){b[b.length-1].Rc.push(a.getAttribute("ref"))},tag:Tr}),Xr=M(Ur,{node:function(a,b){var c=b[0],d=b[b.length-1],e=a.getAttribute("id"),f=[parseFloat(a.getAttribute("lon")),parseFloat(a.getAttribute("lat"))];d.Zg[e]=f;var g=O({Cd:{}},Wr,a,b);Ha(g.Cd)||(f=new C(f),un(f,!1,c),c=new Ik(f),c.mc(e),c.G(g.Cd),d.features.push(c))},way:function(a,b){for(var c=b[0],d=a.getAttribute("id"),e=O({Rc:[],Cd:{}},Vr,a,b),f=b[b.length-1],g=[],h=0,l=e.Rc.length;h<l;h++)mb(g,f.Zg[e.Rc[h]]); +e.Rc[0]==e.Rc[e.Rc.length-1]?(h=new E(null),h.ba("XY",g,[g.length])):(h=new R(null),h.ba("XY",g));un(h,!1,c);c=new Ik(h);c.mc(d);c.G(e.Cd);f.features.push(c)}}),Wr=M(Ur,{tag:Tr});Sr.prototype.lc=function(a,b){var c=sn(this,a,b);return"osm"==a.localName&&(c=O({Zg:{},features:[]},Xr,a,[c]),c.features)?c.features:[]};function Yr(a){return a.getAttributeNS("http://www.w3.org/1999/xlink","href")};function Zr(){}Zr.prototype.read=function(a){return Pk(a)?this.a(a):Qk(a)?this.b(a):"string"===typeof a?(a=Rk(a),this.a(a)):null};function $r(){}y($r,Zr);$r.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};$r.prototype.b=function(a){return(a=O({},as,a,[]))?a:null}; +var bs=[null,"http://www.opengis.net/ows/1.1"],as=M(bs,{ServiceIdentification:J(function(a,b){return O({},cs,a,b)}),ServiceProvider:J(function(a,b){return O({},ds,a,b)}),OperationsMetadata:J(function(a,b){return O({},es,a,b)})}),fs=M(bs,{DeliveryPoint:J(U),City:J(U),AdministrativeArea:J(U),PostalCode:J(U),Country:J(U),ElectronicMailAddress:J(U)}),gs=M(bs,{Value:Vk(function(a){return U(a)})}),hs=M(bs,{AllowedValues:J(function(a,b){return O({},gs,a,b)})}),js=M(bs,{Phone:J(function(a,b){return O({}, +is,a,b)}),Address:J(function(a,b){return O({},fs,a,b)})}),ls=M(bs,{HTTP:J(function(a,b){return O({},ks,a,b)})}),ks=M(bs,{Get:Vk(function(a,b){var c=Yr(a);return c?O({href:c},ms,a,b):void 0}),Post:void 0}),ns=M(bs,{DCP:J(function(a,b){return O({},ls,a,b)})}),es=M(bs,{Operation:function(a,b){var c=a.getAttribute("name"),d=O({},ns,a,b);d&&(b[b.length-1][c]=d)}}),is=M(bs,{Voice:J(U),Facsimile:J(U)}),ms=M(bs,{Constraint:Vk(function(a,b){var c=a.getAttribute("name");return c?O({name:c},hs,a,b):void 0})}), +os=M(bs,{IndividualName:J(U),PositionName:J(U),ContactInfo:J(function(a,b){return O({},js,a,b)})}),cs=M(bs,{Title:J(U),ServiceTypeVersion:J(U),ServiceType:J(U)}),ds=M(bs,{ProviderName:J(U),ProviderSite:J(Yr),ServiceContact:J(function(a,b){return O({},os,a,b)})});function ps(a,b,c,d){var e;void 0!==d?e=d:e=[];for(var f=d=0;f<b;){var g=a[f++];e[d++]=a[f++];e[d++]=g;for(g=2;g<c;++g)e[d++]=a[f++]}e.length=d};function qs(a){a=a?a:{};this.defaultDataProjection=null;this.defaultDataProjection=yc("EPSG:4326");this.b=a.factor?a.factor:1E5;this.a=a.geometryLayout?a.geometryLayout:"XY"}y(qs,bp);function rs(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;var f,g;f=0;for(g=a.length;f<g;)for(d=0;d<b;++d,++f){var h=a[f],l=h-e[d];e[d]=h;a[f]=l}return ss(a,c?c:1E5)} +function ts(a,b,c){var d,e=Array(b);for(d=0;d<b;++d)e[d]=0;a=us(a,c?c:1E5);var f;c=0;for(f=a.length;c<f;)for(d=0;d<b;++d,++c)e[d]+=a[c],a[c]=e[d];return a}function ss(a,b){var c=b?b:1E5,d,e;d=0;for(e=a.length;d<e;++d)a[d]=Math.round(a[d]*c);c=0;for(d=a.length;c<d;++c)e=a[c],a[c]=0>e?~(e<<1):e<<1;c="";d=0;for(e=a.length;d<e;++d){for(var f=a[d],g,h="";32<=f;)g=(32|f&31)+63,h+=String.fromCharCode(g),f>>=5;h+=String.fromCharCode(f+63);c+=h}return c} +function us(a,b){var c=b?b:1E5,d=[],e=0,f=0,g,h;g=0;for(h=a.length;g<h;++g){var l=a.charCodeAt(g)-63,e=e|(l&31)<<f;32>l?(d.push(e),f=e=0):f+=5}e=0;for(f=d.length;e<f;++e)g=d[e],d[e]=g&1?~(g>>1):g>>1;e=0;for(f=d.length;e<f;++e)d[e]/=c;return d}k=qs.prototype;k.ud=function(a,b){var c=this.wd(a,b);return new Ik(c)};k.Kf=function(a,b){return[this.ud(a,b)]};k.wd=function(a,b){var c=id(this.a),d=ts(a,c,this.b);ps(d,d.length,c,d);c=vd(d,0,d.length,c);return un(new R(c,this.a),!1,tn(this,b))}; +k.He=function(a,b){var c=a.W();return c?this.Ed(c,b):""};k.Ci=function(a,b){return this.He(a[0],b)};k.Ed=function(a,b){a=un(a,!0,tn(this,b));var c=a.la(),d=a.va();ps(c,c.length,d,c);return rs(c,d,this.b)};function ws(a){a=a?a:{};this.defaultDataProjection=null;this.defaultDataProjection=yc(a.defaultDataProjection?a.defaultDataProjection:"EPSG:4326")}y(ws,vn);function xs(a,b){var c=[],d,e,f,g;f=0;for(g=a.length;f<g;++f)d=a[f],0<f&&c.pop(),0<=d?e=b[d]:e=b[~d].slice().reverse(),c.push.apply(c,e);d=0;for(e=c.length;d<e;++d)c[d]=c[d].slice();return c}function ys(a,b,c,d,e){a=a.geometries;var f=[],g,h;g=0;for(h=a.length;g<h;++g)f[g]=zs(a[g],b,c,d,e);return f} +function zs(a,b,c,d,e){var f=a.type,g=As[f];b="Point"===f||"MultiPoint"===f?g(a,c,d):g(a,b);c=new Ik;c.Ua(un(b,!1,e));void 0!==a.id&&c.mc(a.id);a.properties&&c.G(a.properties);return c} +ws.prototype.Jf=function(a,b){if("Topology"==a.type){var c,d=null,e=null;a.transform&&(c=a.transform,d=c.scale,e=c.translate);var f=a.arcs;if(c){c=d;var g=e,h,l;h=0;for(l=f.length;h<l;++h){var m=f[h],n=c,p=g,q=0,r=0,u,x,v;x=0;for(v=m.length;x<v;++x)u=m[x],q+=u[0],r+=u[1],u[0]=q,u[1]=r,Bs(u,n,p)}}c=[];g=Ga(a.objects);h=0;for(l=g.length;h<l;++h)"GeometryCollection"===g[h].type?(m=g[h],c.push.apply(c,ys(m,f,d,e,b))):(m=g[h],c.push(zs(m,f,d,e,b)));return c}return[]}; +function Bs(a,b,c){a[0]=a[0]*b[0]+c[0];a[1]=a[1]*b[1]+c[1]}ws.prototype.Oa=function(){return this.defaultDataProjection}; +var As={Point:function(a,b,c){a=a.coordinates;b&&c&&Bs(a,b,c);return new C(a)},LineString:function(a,b){var c=xs(a.arcs,b);return new R(c)},Polygon:function(a,b){var c=[],d,e;d=0;for(e=a.arcs.length;d<e;++d)c[d]=xs(a.arcs[d],b);return new E(c)},MultiPoint:function(a,b,c){a=a.coordinates;var d,e;if(b&&c)for(d=0,e=a.length;d<e;++d)Bs(a[d],b,c);return new Bn(a)},MultiLineString:function(a,b){var c=[],d,e;d=0;for(e=a.arcs.length;d<e;++d)c[d]=xs(a.arcs[d],b);return new S(c)},MultiPolygon:function(a,b){var c= +[],d,e,f,g,h,l;h=0;for(l=a.arcs.length;h<l;++h){d=a.arcs[h];e=[];f=0;for(g=d.length;f<g;++f)e[f]=xs(d[f],b);c[h]=e}return new T(c)}};function Cs(a){a=a?a:{};this.i=a.featureType;this.g=a.featureNS;this.b=a.gmlFormat?a.gmlFormat:new lo;this.c=a.schemaLocation?a.schemaLocation:"http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd";Un.call(this)}y(Cs,Un);Cs.prototype.lc=function(a,b){var c={featureType:this.i,featureNS:this.g};Ea(c,sn(this,a,b?b:{}));c=[c];this.b.b["http://www.opengis.net/gml"].featureMember=Tk(Xn.prototype.vd);(c=O([],this.b.b,a,c,this.b))||(c=[]);return c}; +Cs.prototype.o=function(a){if(Pk(a))return Ds(a);if(Qk(a))return O({},Es,a,[]);if("string"===typeof a)return a=Rk(a),Ds(a)};Cs.prototype.l=function(a){if(Pk(a))return Fs(this,a);if(Qk(a))return Gs(this,a);if("string"===typeof a)return a=Rk(a),Fs(this,a)};function Fs(a,b){for(var c=b.firstChild;c;c=c.nextSibling)if(c.nodeType==Node.ELEMENT_NODE)return Gs(a,c)}var Hs={"http://www.opengis.net/gml":{boundedBy:J(Xn.prototype.ye,"bounds")}}; +function Gs(a,b){var c={},d=fo(b.getAttribute("numberOfFeatures"));c.numberOfFeatures=d;return O(c,Hs,b,[],a.b)} +var Is={"http://www.opengis.net/wfs":{totalInserted:J(eo),totalUpdated:J(eo),totalDeleted:J(eo)}},Js={"http://www.opengis.net/ogc":{FeatureId:Tk(function(a){return a.getAttribute("fid")})}},Ks={"http://www.opengis.net/wfs":{Feature:function(a,b){al(Js,a,b)}}},Es={"http://www.opengis.net/wfs":{TransactionSummary:J(function(a,b){return O({},Is,a,b)},"transactionSummary"),InsertResults:J(function(a,b){return O([],Ks,a,b)},"insertIds")}}; +function Ds(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return O({},Es,a,[])}var Ls={"http://www.opengis.net/wfs":{PropertyName:L(ho)}};function Ms(a,b){var c=Mk("http://www.opengis.net/ogc","Filter"),d=Mk("http://www.opengis.net/ogc","FeatureId");c.appendChild(d);d.setAttribute("fid",b);a.appendChild(c)} +var Ns={"http://www.opengis.net/wfs":{Insert:L(function(a,b,c){var d=c[c.length-1],d=Mk(d.featureNS,d.featureType);a.appendChild(d);lo.prototype.Bi(d,b,c)}),Update:L(function(a,b,c){var d=c[c.length-1],e=d.featureType,f=d.featurePrefix,f=f?f:"feature",g=d.featureNS;a.setAttribute("typeName",f+":"+e);a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+f,g);e=b.Xa();if(void 0!==e){for(var f=b.N(),g=[],h=0,l=f.length;h<l;h++){var m=b.get(f[h]);void 0!==m&&g.push({name:f[h],value:m})}bl({node:a, +srsName:d.srsName},Ns,Xk("Property"),g,c);Ms(a,e)}}),Delete:L(function(a,b,c){var d=c[c.length-1];c=d.featureType;var e=d.featurePrefix,e=e?e:"feature",d=d.featureNS;a.setAttribute("typeName",e+":"+c);a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+e,d);b=b.Xa();void 0!==b&&Ms(a,b)}),Property:L(function(a,b,c){var d=Mk("http://www.opengis.net/wfs","Name");a.appendChild(d);ho(d,b.name);void 0!==b.value&&null!==b.value&&(d=Mk("http://www.opengis.net/wfs","Value"),a.appendChild(d),b.value instanceof +Tc?lo.prototype.Je(d,b.value,c):ho(d,b.value))}),Native:L(function(a,b){b.wp&&a.setAttribute("vendorId",b.wp);void 0!==b.Yo&&a.setAttribute("safeToIgnore",b.Yo);void 0!==b.value&&ho(a,b.value)})}};function Os(a,b,c){a={node:a};var d=b.b;bl(a,Ps,Xk(d.Wb),[d],c);b=b.a;bl(a,Ps,Xk(b.Wb),[b],c)}function Qs(a,b){void 0!==b.a&&a.setAttribute("matchCase",b.a.toString());Rs(a,b.b);Ss("Literal",a,""+b.g)}function Ss(a,b,c){a=Mk("http://www.opengis.net/ogc",a);ho(a,c);b.appendChild(a)} +function Rs(a,b){Ss("PropertyName",a,b)} +var Ps={"http://www.opengis.net/wfs":{Query:L(function(a,b,c){var d=c[c.length-1],e=d.featurePrefix,f=d.featureNS,g=d.propertyNames,h=d.srsName;a.setAttribute("typeName",(e?e+":":"")+b);h&&a.setAttribute("srsName",h);f&&a.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:"+e,f);b=Ea({},d);b.node=a;bl(b,Ls,Xk("PropertyName"),g,c);if(d=d.filter)g=Mk("http://www.opengis.net/ogc","Filter"),a.appendChild(g),bl({node:g},Ps,Xk(d.Wb),[d],c)})},"http://www.opengis.net/ogc":{And:L(Os),Or:L(Os),Not:L(function(a, +b,c){b=b.condition;bl({node:a},Ps,Xk(b.Wb),[b],c)}),BBOX:L(function(a,b,c){c[c.length-1].srsName=b.srsName;Rs(a,b.geometryName);lo.prototype.Je(a,b.extent,c)}),PropertyIsEqualTo:L(Qs),PropertyIsNotEqualTo:L(Qs),PropertyIsLessThan:L(Qs),PropertyIsLessThanOrEqualTo:L(Qs),PropertyIsGreaterThan:L(Qs),PropertyIsGreaterThanOrEqualTo:L(Qs),PropertyIsNull:L(function(a,b){Rs(a,b.b)}),PropertyIsBetween:L(function(a,b){Rs(a,b.b);Ss("LowerBoundary",a,""+b.a);Ss("UpperBoundary",a,""+b.g)}),PropertyIsLike:L(function(a, +b){a.setAttribute("wildCard",b.i);a.setAttribute("singleChar",b.c);a.setAttribute("escapeChar",b.g);void 0!==b.a&&a.setAttribute("matchCase",b.a.toString());Rs(a,b.b);Ss("Literal",a,""+b.f)})}}; +Cs.prototype.j=function(a){var b=Mk("http://www.opengis.net/wfs","GetFeature");b.setAttribute("service","WFS");b.setAttribute("version","1.1.0");var c;if(a&&(a.handle&&b.setAttribute("handle",a.handle),a.outputFormat&&b.setAttribute("outputFormat",a.outputFormat),void 0!==a.maxFeatures&&b.setAttribute("maxFeatures",a.maxFeatures),a.resultType&&b.setAttribute("resultType",a.resultType),void 0!==a.startIndex&&b.setAttribute("startIndex",a.startIndex),void 0!==a.count&&b.setAttribute("count",a.count), +c=a.filter,a.bbox)){var d=Ar(a.geometryName,a.bbox,a.srsName);c?c=yr(c,d):c=d}b.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.c);d=a.featureTypes;a=[{node:b,srsName:a.srsName,featureNS:a.featureNS?a.featureNS:this.g,featurePrefix:a.featurePrefix,geometryName:a.geometryName,filter:c,propertyNames:a.propertyNames?a.propertyNames:[]}];c=Ea({},a[a.length-1]);c.node=b;bl(c,Ps,Xk("Query"),d,a);return b}; +Cs.prototype.U=function(a,b,c,d){var e=[],f=Mk("http://www.opengis.net/wfs","Transaction");f.setAttribute("service","WFS");f.setAttribute("version","1.1.0");var g,h;d&&(g=d.gmlOptions?d.gmlOptions:{},d.handle&&f.setAttribute("handle",d.handle));f.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance","xsi:schemaLocation",this.c);a&&(h={node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},Ea(h,g),bl(h,Ns,Xk("Insert"),a,e));b&&(h={node:f,featureNS:d.featureNS, +featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},Ea(h,g),bl(h,Ns,Xk("Update"),b,e));c&&bl({node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},Ns,Xk("Delete"),c,e);d.nativeElements&&bl({node:f,featureNS:d.featureNS,featureType:d.featureType,featurePrefix:d.featurePrefix,srsName:d.srsName},Ns,Xk("Native"),d.nativeElements,e);return f}; +Cs.prototype.Pf=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.Be(a);return null};Cs.prototype.Be=function(a){if(a.firstElementChild&&a.firstElementChild.firstElementChild)for(a=a.firstElementChild.firstElementChild,a=a.firstElementChild;a;a=a.nextElementSibling)if(0!==a.childNodes.length&&(1!==a.childNodes.length||3!==a.firstChild.nodeType)){var b=[{}];this.b.ye(a,b);return yc(b.pop().srsName)}return null};function Ts(a){a=a?a:{};this.defaultDataProjection=null;this.b=void 0!==a.splitCollection?a.splitCollection:!1}y(Ts,bp);function Us(a){a=a.Z();return 0===a.length?"":a[0]+" "+a[1]}function Vs(a){a=a.Z();for(var b=[],c=0,d=a.length;c<d;++c)b.push(a[c][0]+" "+a[c][1]);return b.join(",")}function Ws(a){var b=[];a=a.Vd();for(var c=0,d=a.length;c<d;++c)b.push("("+Vs(a[c])+")");return b.join(",")}function Xs(a){var b=a.X();a=(0,Ys[b])(a);b=b.toUpperCase();return 0===a.length?b+" EMPTY":b+"("+a+")"} +var Ys={Point:Us,LineString:Vs,Polygon:Ws,MultiPoint:function(a){var b=[];a=a.je();for(var c=0,d=a.length;c<d;++c)b.push("("+Us(a[c])+")");return b.join(",")},MultiLineString:function(a){var b=[];a=a.md();for(var c=0,d=a.length;c<d;++c)b.push("("+Vs(a[c])+")");return b.join(",")},MultiPolygon:function(a){var b=[];a=a.Wd();for(var c=0,d=a.length;c<d;++c)b.push("("+Ws(a[c])+")");return b.join(",")},GeometryCollection:function(a){var b=[];a=a.ff();for(var c=0,d=a.length;c<d;++c)b.push(Xs(a[c]));return b.join(",")}}; +k=Ts.prototype;k.ud=function(a,b){var c=this.wd(a,b);if(c){var d=new Ik;d.Ua(c);return d}return null};k.Kf=function(a,b){var c=[],d=this.wd(a,b);this.b&&"GeometryCollection"==d.X()?c=d.c:c=[d];for(var e=[],f=0,g=c.length;f<g;++f)d=new Ik,d.Ua(c[f]),e.push(d);return e};k.wd=function(a,b){var c;c=new Zs(new $s(a));c.b=at(c.a);return(c=bt(c))?un(c,!1,b):null};k.He=function(a,b){var c=a.W();return c?this.Ed(c,b):""}; +k.Ci=function(a,b){if(1==a.length)return this.He(a[0],b);for(var c=[],d=0,e=a.length;d<e;++d)c.push(a[d].W());c=new Ln(c);return this.Ed(c,b)};k.Ed=function(a,b){return Xs(un(a,!0,b))};function $s(a){this.a=a;this.b=-1} +function at(a){var b=a.a.charAt(++a.b),c={position:a.b,value:b};if("("==b)c.type=2;else if(","==b)c.type=5;else if(")"==b)c.type=3;else if("0"<=b&&"9">=b||"."==b||"-"==b){c.type=4;var d,b=a.b,e=!1,f=!1;do{if("."==d)e=!0;else if("e"==d||"E"==d)f=!0;d=a.a.charAt(++a.b)}while("0"<=d&&"9">=d||"."==d&&(void 0===e||!e)||!f&&("e"==d||"E"==d)||f&&("-"==d||"+"==d));a=parseFloat(a.a.substring(b,a.b--));c.value=a}else if("a"<=b&&"z">=b||"A"<=b&&"Z">=b){c.type=1;b=a.b;do d=a.a.charAt(++a.b);while("a"<=d&&"z">= +d||"A"<=d&&"Z">=d);a=a.a.substring(b,a.b--).toUpperCase();c.value=a}else{if(" "==b||"\t"==b||"\r"==b||"\n"==b)return at(a);if(""===b)c.type=6;else throw Error("Unexpected character: "+b);}return c}function Zs(a){this.a=a}k=Zs.prototype;k.match=function(a){if(a=this.b.type==a)this.b=at(this.a);return a}; +function bt(a){var b=a.b;if(a.match(1)){var c=b.value;if("GEOMETRYCOLLECTION"==c){a:{if(a.match(2)){b=[];do b.push(bt(a));while(a.match(5));if(a.match(3)){a=b;break a}}else if(ct(a)){a=[];break a}throw Error(dt(a));}return new Ln(a)}var d=et[c],b=ft[c];if(!d||!b)throw Error("Invalid geometry type: "+c);a=d.call(a);return new b(a)}throw Error(dt(a));}k.Ef=function(){if(this.match(2)){var a=gt(this);if(this.match(3))return a}else if(ct(this))return null;throw Error(dt(this));}; +k.Df=function(){if(this.match(2)){var a=ht(this);if(this.match(3))return a}else if(ct(this))return[];throw Error(dt(this));};k.Ff=function(){if(this.match(2)){var a=it(this);if(this.match(3))return a}else if(ct(this))return[];throw Error(dt(this));};k.io=function(){if(this.match(2)){var a;if(2==this.b.type)for(a=[this.Ef()];this.match(5);)a.push(this.Ef());else a=ht(this);if(this.match(3))return a}else if(ct(this))return[];throw Error(dt(this));}; +k.ho=function(){if(this.match(2)){var a=it(this);if(this.match(3))return a}else if(ct(this))return[];throw Error(dt(this));};k.jo=function(){if(this.match(2)){for(var a=[this.Ff()];this.match(5);)a.push(this.Ff());if(this.match(3))return a}else if(ct(this))return[];throw Error(dt(this));};function gt(a){for(var b=[],c=0;2>c;++c){var d=a.b;if(a.match(4))b.push(d.value);else break}if(2==b.length)return b;throw Error(dt(a));}function ht(a){for(var b=[gt(a)];a.match(5);)b.push(gt(a));return b} +function it(a){for(var b=[a.Df()];a.match(5);)b.push(a.Df());return b}function ct(a){var b=1==a.b.type&&"EMPTY"==a.b.value;b&&(a.b=at(a.a));return b}function dt(a){return"Unexpected `"+a.b.value+"` at position "+a.b.position+" in `"+a.a.a+"`"}var ft={POINT:C,LINESTRING:R,POLYGON:E,MULTIPOINT:Bn,MULTILINESTRING:S,MULTIPOLYGON:T},et={POINT:Zs.prototype.Ef,LINESTRING:Zs.prototype.Df,POLYGON:Zs.prototype.Ff,MULTIPOINT:Zs.prototype.io,MULTILINESTRING:Zs.prototype.ho,MULTIPOLYGON:Zs.prototype.jo};function jt(){this.version=void 0}y(jt,Zr);jt.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};jt.prototype.b=function(a){this.version=a.getAttribute("version").trim();return(a=O({version:this.version},kt,a,[]))?a:null};function lt(a,b){return O({},mt,a,b)}function nt(a,b){return O({},ot,a,b)}function pt(a,b){var c=lt(a,b);if(c){var d=[fo(a.getAttribute("width")),fo(a.getAttribute("height"))];c.size=d;return c}} +function qt(a,b){return O([],rt,a,b)} +var st=[null,"http://www.opengis.net/wms"],kt=M(st,{Service:J(function(a,b){return O({},tt,a,b)}),Capability:J(function(a,b){return O({},ut,a,b)})}),ut=M(st,{Request:J(function(a,b){return O({},vt,a,b)}),Exception:J(function(a,b){return O([],wt,a,b)}),Layer:J(function(a,b){return O({},xt,a,b)})}),tt=M(st,{Name:J(U),Title:J(U),Abstract:J(U),KeywordList:J(qt),OnlineResource:J(Yr),ContactInformation:J(function(a,b){return O({},yt,a,b)}),Fees:J(U),AccessConstraints:J(U),LayerLimit:J(eo),MaxWidth:J(eo), +MaxHeight:J(eo)}),yt=M(st,{ContactPersonPrimary:J(function(a,b){return O({},zt,a,b)}),ContactPosition:J(U),ContactAddress:J(function(a,b){return O({},At,a,b)}),ContactVoiceTelephone:J(U),ContactFacsimileTelephone:J(U),ContactElectronicMailAddress:J(U)}),zt=M(st,{ContactPerson:J(U),ContactOrganization:J(U)}),At=M(st,{AddressType:J(U),Address:J(U),City:J(U),StateOrProvince:J(U),PostCode:J(U),Country:J(U)}),wt=M(st,{Format:Tk(U)}),xt=M(st,{Name:J(U),Title:J(U),Abstract:J(U),KeywordList:J(qt),CRS:Vk(U), +EX_GeographicBoundingBox:J(function(a,b){var c=O({},Bt,a,b);if(c){var d=c.westBoundLongitude,e=c.southBoundLatitude,f=c.eastBoundLongitude,c=c.northBoundLatitude;return void 0===d||void 0===e||void 0===f||void 0===c?void 0:[d,e,f,c]}}),BoundingBox:Vk(function(a){var b=[co(a.getAttribute("minx")),co(a.getAttribute("miny")),co(a.getAttribute("maxx")),co(a.getAttribute("maxy"))],c=[co(a.getAttribute("resx")),co(a.getAttribute("resy"))];return{crs:a.getAttribute("CRS"),extent:b,res:c}}),Dimension:Vk(function(a){return{name:a.getAttribute("name"), +units:a.getAttribute("units"),unitSymbol:a.getAttribute("unitSymbol"),"default":a.getAttribute("default"),multipleValues:$n(a.getAttribute("multipleValues")),nearestValue:$n(a.getAttribute("nearestValue")),current:$n(a.getAttribute("current")),values:U(a)}}),Attribution:J(function(a,b){return O({},Ct,a,b)}),AuthorityURL:Vk(function(a,b){var c=lt(a,b);if(c)return c.name=a.getAttribute("name"),c}),Identifier:Vk(U),MetadataURL:Vk(function(a,b){var c=lt(a,b);if(c)return c.type=a.getAttribute("type"), +c}),DataURL:Vk(lt),FeatureListURL:Vk(lt),Style:Vk(function(a,b){return O({},Dt,a,b)}),MinScaleDenominator:J(bo),MaxScaleDenominator:J(bo),Layer:Vk(function(a,b){var c=b[b.length-1],d=O({},xt,a,b);if(d){var e=$n(a.getAttribute("queryable"));void 0===e&&(e=c.queryable);d.queryable=void 0!==e?e:!1;e=fo(a.getAttribute("cascaded"));void 0===e&&(e=c.cascaded);d.cascaded=e;e=$n(a.getAttribute("opaque"));void 0===e&&(e=c.opaque);d.opaque=void 0!==e?e:!1;e=$n(a.getAttribute("noSubsets"));void 0===e&&(e=c.noSubsets); +d.noSubsets=void 0!==e?e:!1;(e=co(a.getAttribute("fixedWidth")))||(e=c.fixedWidth);d.fixedWidth=e;(e=co(a.getAttribute("fixedHeight")))||(e=c.fixedHeight);d.fixedHeight=e;["Style","CRS","AuthorityURL"].forEach(function(a){a in c&&(d[a]=(d[a]||[]).concat(c[a]))});"EX_GeographicBoundingBox BoundingBox Dimension Attribution MinScaleDenominator MaxScaleDenominator".split(" ").forEach(function(a){a in d||(d[a]=c[a])});return d}})}),Ct=M(st,{Title:J(U),OnlineResource:J(Yr),LogoURL:J(pt)}),Bt=M(st,{westBoundLongitude:J(bo), +eastBoundLongitude:J(bo),southBoundLatitude:J(bo),northBoundLatitude:J(bo)}),vt=M(st,{GetCapabilities:J(nt),GetMap:J(nt),GetFeatureInfo:J(nt)}),ot=M(st,{Format:Vk(U),DCPType:Vk(function(a,b){return O({},Et,a,b)})}),Et=M(st,{HTTP:J(function(a,b){return O({},Ft,a,b)})}),Ft=M(st,{Get:J(lt),Post:J(lt)}),Dt=M(st,{Name:J(U),Title:J(U),Abstract:J(U),LegendURL:Vk(pt),StyleSheetURL:J(lt),StyleURL:J(lt)}),mt=M(st,{Format:J(U),OnlineResource:J(Yr)}),rt=M(st,{Keyword:Tk(U)});function Gt(a){a=a?a:{};this.g="http://mapserver.gis.umn.edu/mapserver";this.b=new ko;this.c=a.layers?a.layers:null;Un.call(this)}y(Gt,Un); +Gt.prototype.lc=function(a,b){var c={};b&&Ea(c,sn(this,a,b));var d=[c];a.setAttribute("namespaceURI",this.g);var e=a.localName,c=[];if(0!==a.childNodes.length){if("msGMLOutput"==e)for(var f=0,g=a.childNodes.length;f<g;f++){var h=a.childNodes[f];if(h.nodeType===Node.ELEMENT_NODE){var l=d[0],m=h.localName.replace("_layer","");if(!this.c||jb(this.c,m)){m+="_feature";l.featureType=m;l.featureNS=this.g;var n={};n[m]=Tk(this.b.If,this.b);l=M([l.featureNS,null],n);h.setAttribute("namespaceURI",this.g);(h= +O([],l,h,d,this.b))&&mb(c,h)}}}"FeatureCollection"==e&&(d=O([],this.b.b,a,[{}],this.b))&&(c=d)}return c};function Ht(){this.g=new $r}y(Ht,Zr);Ht.prototype.a=function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType==Node.ELEMENT_NODE)return this.b(a);return null};Ht.prototype.b=function(a){var b=a.getAttribute("version").trim(),c=this.g.b(a);if(!c)return null;c.version=b;return(c=O(c,It,a,[]))?c:null};function Jt(a){var b=U(a).split(" ");if(b&&2==b.length)return a=+b[0],b=+b[1],isNaN(a)||isNaN(b)?void 0:[a,b]} +var Kt=[null,"http://www.opengis.net/wmts/1.0"],Lt=[null,"http://www.opengis.net/ows/1.1"],It=M(Kt,{Contents:J(function(a,b){return O({},Mt,a,b)})}),Mt=M(Kt,{Layer:Vk(function(a,b){return O({},Nt,a,b)}),TileMatrixSet:Vk(function(a,b){return O({},Ot,a,b)})}),Nt=M(Kt,{Style:Vk(function(a,b){var c=O({},Pt,a,b);if(c){var d="true"===a.getAttribute("isDefault");c.isDefault=d;return c}}),Format:Vk(U),TileMatrixSetLink:Vk(function(a,b){return O({},Qt,a,b)}),Dimension:Vk(function(a,b){return O({},Rt,a,b)}), +ResourceURL:Vk(function(a){var b=a.getAttribute("format"),c=a.getAttribute("template");a=a.getAttribute("resourceType");var d={};b&&(d.format=b);c&&(d.template=c);a&&(d.resourceType=a);return d})},M(Lt,{Title:J(U),Abstract:J(U),WGS84BoundingBox:J(function(a,b){var c=O([],St,a,b);return 2!=c.length?void 0:Kb(c)}),Identifier:J(U)})),Pt=M(Kt,{LegendURL:Vk(function(a){var b={};b.format=a.getAttribute("format");b.href=Yr(a);return b})},M(Lt,{Title:J(U),Identifier:J(U)})),Qt=M(Kt,{TileMatrixSet:J(U)}), +Rt=M(Kt,{Default:J(U),Value:Vk(U)},M(Lt,{Identifier:J(U)})),St=M(Lt,{LowerCorner:Tk(Jt),UpperCorner:Tk(Jt)}),Ot=M(Kt,{WellKnownScaleSet:J(U),TileMatrix:Vk(function(a,b){return O({},Tt,a,b)})},M(Lt,{SupportedCRS:J(U),Identifier:J(U)})),Tt=M(Kt,{TopLeftCorner:J(Jt),ScaleDenominator:J(bo),TileWidth:J(eo),TileHeight:J(eo),MatrixWidth:J(eo),MatrixHeight:J(eo)},M(Lt,{Identifier:J(U)}));function Ut(a){eb.call(this);a=a||{};this.a=null;this.c=Qc;this.f=void 0;B(this,gb("projection"),this.Nl,this);B(this,gb("tracking"),this.Ol,this);void 0!==a.projection&&this.dh(yc(a.projection));void 0!==a.trackingOptions&&this.si(a.trackingOptions);this.ge(void 0!==a.tracking?a.tracking:!1)}y(Ut,eb);k=Ut.prototype;k.ka=function(){this.ge(!1);eb.prototype.ka.call(this)};k.Nl=function(){var a=this.ah();a&&(this.c=Bc(yc("EPSG:4326"),a),this.a&&this.set("position",this.c(this.a)))}; +k.Ol=function(){if(kg){var a=this.bh();a&&void 0===this.f?this.f=pa.navigator.geolocation.watchPosition(this.qo.bind(this),this.ro.bind(this),this.Mg()):a||void 0===this.f||(pa.navigator.geolocation.clearWatch(this.f),this.f=void 0)}}; +k.qo=function(a){a=a.coords;this.set("accuracy",a.accuracy);this.set("altitude",null===a.altitude?void 0:a.altitude);this.set("altitudeAccuracy",null===a.altitudeAccuracy?void 0:a.altitudeAccuracy);this.set("heading",null===a.heading?void 0:wa(a.heading));this.a?(this.a[0]=a.longitude,this.a[1]=a.latitude):this.a=[a.longitude,a.latitude];var b=this.c(this.a);this.set("position",b);this.set("speed",null===a.speed?void 0:a.speed);a=Nd(Yi,this.a,a.accuracy);a.rc(this.c);this.set("accuracyGeometry",a); +this.u()};k.ro=function(a){a.type="error";this.ge(!1);this.b(a)};k.Mj=function(){return this.get("accuracy")};k.Nj=function(){return this.get("accuracyGeometry")||null};k.Pj=function(){return this.get("altitude")};k.Qj=function(){return this.get("altitudeAccuracy")};k.Ll=function(){return this.get("heading")};k.Ml=function(){return this.get("position")};k.ah=function(){return this.get("projection")};k.wk=function(){return this.get("speed")};k.bh=function(){return this.get("tracking")};k.Mg=function(){return this.get("trackingOptions")}; +k.dh=function(a){this.set("projection",a)};k.ge=function(a){this.set("tracking",a)};k.si=function(a){this.set("trackingOptions",a)};function Vt(a,b,c){hd.call(this);this.Vf(a,b?b:0,c)}y(Vt,hd);k=Vt.prototype;k.clone=function(){var a=new Vt(null),b=this.B.slice();jd(a,this.f,b);a.u();return a};k.sb=function(a,b,c,d){var e=this.B;a-=e[0];var f=b-e[1];b=a*a+f*f;if(b<d){if(0===b)for(d=0;d<this.a;++d)c[d]=e[d];else for(d=this.wf()/Math.sqrt(b),c[0]=e[0]+d*a,c[1]=e[1]+d*f,d=2;d<this.a;++d)c[d]=e[d];c.length=this.a;return b}return d};k.Bc=function(a,b){var c=this.B,d=a-c[0],c=b-c[1];return d*d+c*c<=Wt(this)}; +k.rd=function(){return this.B.slice(0,this.a)};k.Od=function(a){var b=this.B,c=b[this.a]-b[0];return Wb(b[0]-c,b[1]-c,b[0]+c,b[1]+c,a)};k.wf=function(){return Math.sqrt(Wt(this))};function Wt(a){var b=a.B[a.a]-a.B[0];a=a.B[a.a+1]-a.B[1];return b*b+a*a}k.X=function(){return"Circle"};k.Ka=function(a){var b=this.H();return nc(a,b)?(b=this.rd(),a[0]<=b[0]&&a[2]>=b[0]||a[1]<=b[1]&&a[3]>=b[1]?!0:bc(a,this.sg,this)):!1}; +k.jm=function(a){var b=this.a,c=this.B[b]-this.B[0],d=a.slice();d[b]=d[0]+c;for(c=1;c<b;++c)d[b+c]=a[c];jd(this,this.f,d);this.u()};k.Vf=function(a,b,c){if(a){kd(this,c,a,0);this.B||(this.B=[]);c=this.B;a=sd(c,a);c[a++]=c[0]+b;var d;b=1;for(d=this.a;b<d;++b)c[a++]=c[b];c.length=a}else jd(this,"XY",null);this.u()};k.km=function(a){this.B[this.a]=this.B[0]+a;this.u()};function Xt(a,b,c){for(var d=[],e=a(0),f=a(1),g=b(e),h=b(f),l=[f,e],m=[h,g],n=[1,0],p={},q=1E5,r,u,x,v,D;0<--q&&0<n.length;)x=n.pop(),e=l.pop(),g=m.pop(),f=x.toString(),f in p||(d.push(g[0],g[1]),p[f]=!0),v=n.pop(),f=l.pop(),h=m.pop(),D=(x+v)/2,r=a(D),u=b(r),ua(u[0],u[1],g[0],g[1],h[0],h[1])<c?(d.push(h[0],h[1]),f=v.toString(),p[f]=!0):(n.push(v,D,D,x),m.push(h,u,u,g),l.push(f,r,r,e));return d}function Yt(a,b,c,d,e){var f=yc("EPSG:4326");return Xt(function(d){return[a,b+(c-b)*d]},Pc(f,d),e)} +function Zt(a,b,c,d,e){var f=yc("EPSG:4326");return Xt(function(d){return[b+(c-b)*d,a]},Pc(f,d),e)};function $t(a){a=a||{};this.c=this.o=null;this.g=this.i=Infinity;this.f=this.l=-Infinity;this.A=this.U=Infinity;this.D=this.C=-Infinity;this.Ba=void 0!==a.targetSize?a.targetSize:100;this.R=void 0!==a.maxLines?a.maxLines:100;this.b=[];this.a=[];this.ya=void 0!==a.strokeStyle?a.strokeStyle:au;this.v=this.j=void 0;this.s=null;this.setMap(void 0!==a.map?a.map:null)}var au=new oj({color:"rgba(0,0,0,0.2)"}),bu=[90,45,30,20,10,5,2,1,.5,.2,.1,.05,.01,.005,.002,.001]; +function cu(a,b,c,d,e,f,g){var h=g;b=Yt(b,c,d,a.c,e);h=void 0!==a.b[h]?a.b[h]:new R(null);h.ba("XY",b);nc(h.H(),f)&&(a.b[g++]=h);return g}function du(a,b,c,d,e){var f=e;b=Zt(b,a.f,a.g,a.c,c);f=void 0!==a.a[f]?a.a[f]:new R(null);f.ba("XY",b);nc(f.H(),d)&&(a.a[e++]=f);return e}k=$t.prototype;k.Pl=function(){return this.o};k.ik=function(){return this.b};k.qk=function(){return this.a}; +k.Rg=function(a){var b=a.vectorContext,c=a.frameState,d=c.extent;a=c.viewState;var e=a.center,f=a.projection,g=a.resolution;a=c.pixelRatio;a=g*g/(4*a*a);if(!this.c||!Oc(this.c,f)){var h=yc("EPSG:4326"),l=f.H(),m=f.i,n=Sc(m,h,f),p=m[2],q=m[1],r=m[0],u=n[3],x=n[2],v=n[1],n=n[0];this.i=m[3];this.g=p;this.l=q;this.f=r;this.U=u;this.A=x;this.C=v;this.D=n;this.j=Pc(h,f);this.v=Pc(f,h);this.s=this.v(kc(l));this.c=f}f.a&&(f=f.H(),h=ic(f),c=c.focus[0],c<f[0]||c>f[2])&&(c=h*Math.ceil((f[0]-c)/h),d=[d[0]+c, +d[1],d[2]+c,d[3]]);c=this.s[0];f=this.s[1];h=-1;m=Math.pow(this.Ba*g,2);p=[];q=[];g=0;for(l=bu.length;g<l;++g){r=bu[g]/2;p[0]=c-r;p[1]=f-r;q[0]=c+r;q[1]=f+r;this.j(p,p);this.j(q,q);r=Math.pow(q[0]-p[0],2)+Math.pow(q[1]-p[1],2);if(r<=m)break;h=bu[g]}g=h;if(-1==g)this.b.length=this.a.length=0;else{c=this.v(e);e=c[0];c=c[1];f=this.R;h=[Math.max(d[0],this.D),Math.max(d[1],this.C),Math.min(d[2],this.A),Math.min(d[3],this.U)];h=Sc(h,this.c,"EPSG:4326");m=h[3];q=h[1];e=Math.floor(e/g)*g;p=sa(e,this.f,this.g); +l=cu(this,p,q,m,a,d,0);for(h=0;p!=this.f&&h++<f;)p=Math.max(p-g,this.f),l=cu(this,p,q,m,a,d,l);p=sa(e,this.f,this.g);for(h=0;p!=this.g&&h++<f;)p=Math.min(p+g,this.g),l=cu(this,p,q,m,a,d,l);this.b.length=l;c=Math.floor(c/g)*g;e=sa(c,this.l,this.i);l=du(this,e,a,d,0);for(h=0;e!=this.l&&h++<f;)e=Math.max(e-g,this.l),l=du(this,e,a,d,l);e=sa(c,this.l,this.i);for(h=0;e!=this.i&&h++<f;)e=Math.min(e+g,this.i),l=du(this,e,a,d,l);this.a.length=l}b.Sb(null,this.ya);a=0;for(e=this.b.length;a<e;++a)g=this.b[a], +b.hd(g,null);a=0;for(e=this.a.length;a<e;++a)g=this.a[a],b.hd(g,null)};k.setMap=function(a){this.o&&(this.o.J("postcompose",this.Rg,this),this.o.render());a&&(a.I("postcompose",this.Rg,this),a.render());this.o=a};function eu(a,b,c,d,e,f,g){oh.call(this,a,b,c,0,d);this.j=e;this.g=new Image;null!==f&&(this.g.crossOrigin=f);this.i={};this.c=null;this.state=0;this.o=g}y(eu,oh);eu.prototype.a=function(a){if(void 0!==a){var b;a=w(a);if(a in this.i)return this.i[a];Ha(this.i)?b=this.g:b=this.g.cloneNode(!1);return this.i[a]=b}return this.g};eu.prototype.s=function(){this.state=3;this.c.forEach(Ka);this.c=null;ph(this)}; +eu.prototype.v=function(){void 0===this.resolution&&(this.resolution=jc(this.extent)/this.g.height);this.state=2;this.c.forEach(Ka);this.c=null;ph(this)};eu.prototype.load=function(){if(0==this.state||3==this.state)this.state=1,ph(this),this.c=[Pa(this.g,"error",this.s,this),Pa(this.g,"load",this.v,this)],this.o(this,this.j)};function fu(a,b,c,d,e){df.call(this,a,b);this.s=c;this.g=new Image;null!==d&&(this.g.crossOrigin=d);this.c={};this.j=null;this.v=e}y(fu,df);k=fu.prototype;k.ka=function(){1==this.state&&gu(this);this.a&&Ta(this.a);this.state=5;ef(this);df.prototype.ka.call(this)};k.$a=function(a){if(void 0!==a){var b=w(a);if(b in this.c)return this.c[b];a=Ha(this.c)?this.g:this.g.cloneNode(!1);return this.c[b]=a}return this.g};k.ib=function(){return this.s};k.Ql=function(){this.state=3;gu(this);ef(this)}; +k.Rl=function(){this.state=this.g.naturalWidth&&this.g.naturalHeight?2:4;gu(this);ef(this)};k.load=function(){if(0==this.state||3==this.state)this.state=1,ef(this),this.j=[Pa(this.g,"error",this.Ql,this),Pa(this.g,"load",this.Rl,this)],this.v(this,this.s)};function gu(a){a.j.forEach(Ka);a.j=null};function hu(a){a=a?a:{};Vh.call(this,{handleEvent:qc});this.c=a.formatConstructors?a.formatConstructors:[];this.j=a.projection?yc(a.projection):null;this.a=null;this.target=a.target?a.target:null}y(hu,Vh);function iu(a){a=a.dataTransfer.files;var b,c,d;b=0;for(c=a.length;b<c;++b){d=a.item(b);var e=new FileReader;e.addEventListener("load",this.o.bind(this,d));e.readAsText(d)}}function ju(a){a.stopPropagation();a.preventDefault();a.dataTransfer.dropEffect="copy"} +hu.prototype.o=function(a,b){var c=b.target.result,d=this.v,e=this.j;e||(e=d.aa().l);var d=this.c,f=[],g,h;g=0;for(h=d.length;g<h;++g){var l=new d[g];var m={featureProjection:e};try{f=l.Fa(c,m)}catch(n){f=null}if(f&&0<f.length)break}this.b(new ku(lu,this,a,f,e))};hu.prototype.setMap=function(a){this.a&&(this.a.forEach(Ka),this.a=null);Vh.prototype.setMap.call(this,a);a&&(a=this.target?this.target:a.a,this.a=[B(a,"drop",iu,this),B(a,"dragenter",ju,this),B(a,"dragover",ju,this),B(a,"drop",ju,this)])}; +var lu="addfeatures";function ku(a,b,c,d,e){Wa.call(this,a,b);this.features=d;this.file=c;this.projection=e}y(ku,Wa);function mu(a){a=a?a:{};ji.call(this,{handleDownEvent:nu,handleDragEvent:ou,handleUpEvent:pu});this.s=a.condition?a.condition:fi;this.a=this.c=void 0;this.j=0;this.A=void 0!==a.duration?a.duration:400}y(mu,ji); +function ou(a){if(hi(a)){var b=a.map,c=b.Za(),d=a.pixel;a=d[0]-c[0]/2;d=c[1]/2-d[1];c=Math.atan2(d,a);a=Math.sqrt(a*a+d*d);d=b.aa();b.render();if(void 0!==this.c){var e=c-this.c;Wh(b,d,d.La()-e)}this.c=c;void 0!==this.a&&(c=this.a*(d.$()/a),Yh(b,d,c));void 0!==this.a&&(this.j=this.a/a);this.a=a}} +function pu(a){if(!hi(a))return!0;a=a.map;var b=a.aa();Xd(b,-1);var c=this.j-1,d=b.La(),d=b.constrainRotation(d,0);Wh(a,b,d,void 0,void 0);var d=b.$(),e=this.A,d=b.constrainResolution(d,0,c);Yh(a,b,d,void 0,e);this.j=0;return!1}function nu(a){return hi(a)&&this.s(a)?(Xd(a.map.aa(),1),this.a=this.c=void 0,!0):!1};function qu(a,b){Wa.call(this,a);this.feature=b}y(qu,Wa); +function ru(a){ji.call(this,{handleDownEvent:su,handleEvent:tu,handleUpEvent:uu});this.za=null;this.S=!1;this.Hc=a.source?a.source:null;this.qb=a.features?a.features:null;this.Cj=a.snapTolerance?a.snapTolerance:12;this.Y=a.type;this.c=vu(this.Y);this.Sa=a.minPoints?a.minPoints:this.c===wu?3:2;this.Aa=a.maxPoints?a.maxPoints:Infinity;this.Ne=a.finishCondition?a.finishCondition:qc;var b=a.geometryFunction;if(!b)if("Circle"===this.Y)b=function(a,b){var c=b?b:new Vt([NaN,NaN]);c.Vf(a[0],Math.sqrt(Hb(a[0], +a[1])));return c};else{var c,b=this.c;b===xu?c=C:b===yu?c=R:b===wu&&(c=E);b=function(a,b){var f=b;f?f.pa(a):f=new c(a);return f}}this.D=b;this.T=this.A=this.a=this.R=this.j=this.s=null;this.Fj=a.clickTolerance?a.clickTolerance*a.clickTolerance:36;this.qa=new G({source:new P({useSpatialIndex:!1,wrapX:a.wrapX?a.wrapX:!1}),style:a.style?a.style:zu()});this.Hb=a.geometryName;this.Bj=a.condition?a.condition:ei;this.ta=a.freehandCondition?a.freehandCondition:fi;B(this,gb("active"),this.yi,this)}y(ru,ji); +function zu(){var a=wj();return function(b){return a[b.W().X()]}}k=ru.prototype;k.setMap=function(a){ji.prototype.setMap.call(this,a);this.yi()};function tu(a){this.c!==yu&&this.c!==wu||!this.ta(a)||(this.S=!0);var b=!this.S;this.S&&a.type===gh?(Au(this,a),b=!1):a.type===fh?b=Bu(this,a):a.type===$g&&(b=!1);return ki.call(this,a)&&b}function su(a){return this.Bj(a)?(this.za=a.pixel,!0):this.S?(this.za=a.pixel,this.s||Cu(this,a),!0):!1} +function uu(a){this.S=!1;var b=this.za,c=a.pixel,d=b[0]-c[0],b=b[1]-c[1],c=!0;d*d+b*b<=this.Fj&&(Bu(this,a),this.s?this.c===Du?this.jd():Eu(this,a)?this.Ne(a)&&this.jd():Au(this,a):(Cu(this,a),this.c===xu&&this.jd()),c=!1);return c} +function Bu(a,b){if(a.s){var c=b.coordinate,d=a.j.W(),e;a.c===xu?e=a.a:a.c===wu?(e=a.a[0],e=e[e.length-1],Eu(a,b)&&(c=a.s.slice())):(e=a.a,e=e[e.length-1]);e[0]=c[0];e[1]=c[1];a.D(a.a,d);a.R&&a.R.W().pa(c);d instanceof E&&a.c!==wu?(a.A||(a.A=new Ik(new R(null))),d=d.Hg(0),c=a.A.W(),c.ba(d.f,d.la())):a.T&&(c=a.A.W(),c.pa(a.T));Fu(a)}else c=b.coordinate.slice(),a.R?a.R.W().pa(c):(a.R=new Ik(new C(c)),Fu(a));return!0} +function Eu(a,b){var c=!1;if(a.j){var d=!1,e=[a.s];a.c===yu?d=a.a.length>a.Sa:a.c===wu&&(d=a.a[0].length>a.Sa,e=[a.a[0][0],a.a[0][a.a[0].length-2]]);if(d)for(var d=b.map,f=0,g=e.length;f<g;f++){var h=e[f],l=d.Ga(h),m=b.pixel,c=m[0]-l[0],l=m[1]-l[1],m=a.S&&a.ta(b)?1:a.Cj;if(c=Math.sqrt(c*c+l*l)<=m){a.s=h;break}}}return c} +function Cu(a,b){var c=b.coordinate;a.s=c;a.c===xu?a.a=c.slice():a.c===wu?(a.a=[[c.slice(),c.slice()]],a.T=a.a[0]):(a.a=[c.slice(),c.slice()],a.c===Du&&(a.T=a.a));a.T&&(a.A=new Ik(new R(a.T)));c=a.D(a.a);a.j=new Ik;a.Hb&&a.j.Ec(a.Hb);a.j.Ua(c);Fu(a);a.b(new qu("drawstart",a.j))} +function Au(a,b){var c=b.coordinate,d=a.j.W(),e,f;if(a.c===yu)a.s=c.slice(),f=a.a,f.push(c.slice()),e=f.length>a.Aa,a.D(f,d);else if(a.c===wu){f=a.a[0];f.push(c.slice());if(e=f.length>a.Aa)a.s=f[0];a.D(a.a,d)}Fu(a);e&&a.jd()}k.Qo=function(){var a=this.j.W(),b,c;this.c===yu?(b=this.a,b.splice(-2,1),this.D(b,a)):this.c===wu&&(b=this.a[0],b.splice(-2,1),c=this.A.W(),c.pa(b),this.D(this.a,a));0===b.length&&(this.s=null);Fu(this)}; +k.jd=function(){var a=Gu(this),b=this.a,c=a.W();this.c===yu?(b.pop(),this.D(b,c)):this.c===wu&&(b[0].pop(),b[0].push(b[0][0]),this.D(b,c));"MultiPoint"===this.Y?a.Ua(new Bn([b])):"MultiLineString"===this.Y?a.Ua(new S([b])):"MultiPolygon"===this.Y&&a.Ua(new T([b]));this.b(new qu("drawend",a));this.qb&&this.qb.push(a);this.Hc&&this.Hc.rb(a)};function Gu(a){a.s=null;var b=a.j;b&&(a.j=null,a.R=null,a.A=null,a.qa.ha().clear(!0));return b} +k.rm=function(a){var b=a.W();this.j=a;this.a=b.Z();a=this.a[this.a.length-1];this.s=a.slice();this.a.push(a.slice());Fu(this);this.b(new qu("drawstart",this.j))};k.Gc=rc;function Fu(a){var b=[];a.j&&b.push(a.j);a.A&&b.push(a.A);a.R&&b.push(a.R);a=a.qa.ha();a.clear(!0);a.Jc(b)}k.yi=function(){var a=this.v,b=this.f();a&&b||Gu(this);this.qa.setMap(b?a:null)}; +function vu(a){var b;"Point"===a||"MultiPoint"===a?b=xu:"LineString"===a||"MultiLineString"===a?b=yu:"Polygon"===a||"MultiPolygon"===a?b=wu:"Circle"===a&&(b=Du);return b}var xu="Point",yu="LineString",wu="Polygon",Du="Circle";function Hu(a,b,c){Wa.call(this,a);this.features=b;this.mapBrowserEvent=c}y(Hu,Wa); +function Iu(a){ji.call(this,{handleDownEvent:Ju,handleDragEvent:Ku,handleEvent:Lu,handleUpEvent:Mu});this.Hb=a.condition?a.condition:ii;this.Sa=function(a){return ei(a)&&di(a)};this.qb=a.deleteCondition?a.deleteCondition:this.Sa;this.Aa=this.c=null;this.qa=[0,0];this.D=this.T=!1;this.a=new kl;this.R=void 0!==a.pixelTolerance?a.pixelTolerance:10;this.s=this.ta=!1;this.j=[];this.S=new G({source:new P({useSpatialIndex:!1,wrapX:!!a.wrapX}),style:a.style?a.style:Nu(),updateWhileAnimating:!0,updateWhileInteracting:!0}); +this.za={Point:this.ym,LineString:this.kh,LinearRing:this.kh,Polygon:this.zm,MultiPoint:this.wm,MultiLineString:this.vm,MultiPolygon:this.xm,GeometryCollection:this.um};this.A=a.features;this.A.forEach(this.xf,this);B(this.A,"add",this.sm,this);B(this.A,"remove",this.tm,this);this.Y=null}y(Iu,ji);k=Iu.prototype;k.xf=function(a){var b=a.W();b.X()in this.za&&this.za[b.X()].call(this,a,b);(b=this.v)&&Ou(this,this.qa,b);B(a,"change",this.jh,this)}; +function Pu(a,b){a.D||(a.D=!0,a.b(new Hu("modifystart",a.A,b)))}function Qu(a,b){Ru(a,b);a.c&&0===a.A.dc()&&(a.S.ha().nb(a.c),a.c=null);Qa(b,"change",a.jh,a)}function Ru(a,b){var c=a.a,d=[];c.forEach(function(a){b===a.feature&&d.push(a)});for(var e=d.length-1;0<=e;--e)c.remove(d[e])}k.setMap=function(a){this.S.setMap(a);ji.prototype.setMap.call(this,a)};k.sm=function(a){this.xf(a.element)};k.jh=function(a){this.s||(a=a.target,Qu(this,a),this.xf(a))};k.tm=function(a){Qu(this,a.element)}; +k.ym=function(a,b){var c=b.Z(),c={feature:a,geometry:b,na:[c,c]};this.a.Ca(b.H(),c)};k.wm=function(a,b){var c=b.Z(),d,e,f;e=0;for(f=c.length;e<f;++e)d=c[e],d={feature:a,geometry:b,depth:[e],index:e,na:[d,d]},this.a.Ca(b.H(),d)};k.kh=function(a,b){var c=b.Z(),d,e,f,g;d=0;for(e=c.length-1;d<e;++d)f=c.slice(d,d+2),g={feature:a,geometry:b,index:d,na:f},this.a.Ca(Kb(f),g)}; +k.vm=function(a,b){var c=b.Z(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,geometry:b,depth:[g],index:e,na:l},this.a.Ca(Kb(l),m)};k.zm=function(a,b){var c=b.Z(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,geometry:b,depth:[g],index:e,na:l},this.a.Ca(Kb(l),m)}; +k.xm=function(a,b){var c=b.Z(),d,e,f,g,h,l,m,n,p,q;l=0;for(m=c.length;l<m;++l)for(n=c[l],g=0,h=n.length;g<h;++g)for(d=n[g],e=0,f=d.length-1;e<f;++e)p=d.slice(e,e+2),q={feature:a,geometry:b,depth:[g,l],index:e,na:p},this.a.Ca(Kb(p),q)};k.um=function(a,b){var c,d=b.c;for(c=0;c<d.length;++c)this.za[d[c].X()].call(this,a,d[c])};function Su(a,b){var c=a.c;c?c.W().pa(b):(c=new Ik(new C(b)),a.c=c,a.S.ha().rb(c))}function Tu(a,b){return a.index-b.index} +function Ju(a){if(!this.Hb(a))return!1;Ou(this,a.pixel,a.map);this.j.length=0;this.D=!1;var b=this.c;if(b){var c=[],b=b.W().Z(),d=Kb([b]),d=nl(this.a,d),e={};d.sort(Tu);for(var f=0,g=d.length;f<g;++f){var h=d[f],l=h.na,m=w(h.feature),n=h.depth;n&&(m+="-"+n.join("-"));e[m]||(e[m]=Array(2));if(Fb(l[0],b)&&!e[m][0])this.j.push([h,0]),e[m][0]=h;else if(Fb(l[1],b)&&!e[m][1]){if("LineString"!==h.geometry.X()&&"MultiLineString"!==h.geometry.X()||!e[m][0]||0!==e[m][0].index)this.j.push([h,1]),e[m][1]=h}else w(l)in +this.Aa&&!e[m][0]&&!e[m][1]&&c.push([h,b])}c.length&&Pu(this,a);for(a=c.length-1;0<=a;--a)this.nl.apply(this,c[a])}return!!this.c} +function Ku(a){this.T=!1;Pu(this,a);a=a.coordinate;for(var b=0,c=this.j.length;b<c;++b){for(var d=this.j[b],e=d[0],f=e.depth,g=e.geometry,h=g.Z(),l=e.na,d=d[1];a.length<g.va();)a.push(0);switch(g.X()){case "Point":h=a;l[0]=l[1]=a;break;case "MultiPoint":h[e.index]=a;l[0]=l[1]=a;break;case "LineString":h[e.index+d]=a;l[d]=a;break;case "MultiLineString":h[f[0]][e.index+d]=a;l[d]=a;break;case "Polygon":h[f[0]][e.index+d]=a;l[d]=a;break;case "MultiPolygon":h[f[1]][f[0]][e.index+d]=a,l[d]=a}e=g;this.s= +!0;e.pa(h);this.s=!1}Su(this,a)}function Mu(a){for(var b,c=this.j.length-1;0<=c;--c)b=this.j[c][0],ll(this.a,Kb(b.na),b);this.D&&(this.b(new Hu("modifyend",this.A,a)),this.D=!1);return!1}function Lu(a){if(!(a instanceof Wg))return!0;this.Y=a;var b;Sd(a.map.aa())[1]||a.type!=fh||this.C||(this.qa=a.pixel,Ou(this,a.pixel,a.map));this.c&&this.qb(a)&&(a.type==ah&&this.T?b=!0:(this.c.W(),b=this.ai()));a.type==ah&&(this.T=!1);return ki.call(this,a)&&!b} +function Ou(a,b,c){function d(a,b){return Ib(e,a.na)-Ib(e,b.na)}var e=c.Ma(b),f=c.Ma([b[0]-a.R,b[1]+a.R]),g=c.Ma([b[0]+a.R,b[1]-a.R]),f=Kb([f,g]),f=nl(a.a,f);if(0<f.length){f.sort(d);var g=f[0].na,h=Cb(e,g),l=c.Ga(h);if(Math.sqrt(Hb(b,l))<=a.R){b=c.Ga(g[0]);c=c.Ga(g[1]);b=Hb(l,b);c=Hb(l,c);a.ta=Math.sqrt(Math.min(b,c))<=a.R;a.ta&&(h=b>c?g[1]:g[0]);Su(a,h);c={};c[w(g)]=!0;b=1;for(l=f.length;b<l;++b)if(h=f[b].na,Fb(g[0],h[0])&&Fb(g[1],h[1])||Fb(g[0],h[1])&&Fb(g[1],h[0]))c[w(h)]=!0;else break;a.Aa=c; +return}}a.c&&(a.S.ha().nb(a.c),a.c=null)} +k.nl=function(a,b){for(var c=a.na,d=a.feature,e=a.geometry,f=a.depth,g=a.index,h;b.length<e.va();)b.push(0);switch(e.X()){case "MultiLineString":h=e.Z();h[f[0]].splice(g+1,0,b);break;case "Polygon":h=e.Z();h[f[0]].splice(g+1,0,b);break;case "MultiPolygon":h=e.Z();h[f[1]][f[0]].splice(g+1,0,b);break;case "LineString":h=e.Z();h.splice(g+1,0,b);break;default:return}this.s=!0;e.pa(h);this.s=!1;h=this.a;h.remove(a);Uu(this,e,g,f,1);var l={na:[c[0],b],feature:d,geometry:e,depth:f,index:g};h.Ca(Kb(l.na), +l);this.j.push([l,1]);c={na:[b,c[1]],feature:d,geometry:e,depth:f,index:g+1};h.Ca(Kb(c.na),c);this.j.push([c,0]);this.T=!0}; +k.ai=function(){var a=!1;if(this.Y&&this.Y.type!=gh){var b=this.Y;Pu(this,b);var c=this.j,a={},d,e,f,g,h,l,m,n,p;for(g=c.length-1;0<=g;--g)f=c[g],m=f[0],n=w(m.feature),m.depth&&(n+="-"+m.depth.join("-")),n in a||(a[n]={}),0===f[1]?(a[n].right=m,a[n].index=m.index):1==f[1]&&(a[n].left=m,a[n].index=m.index+1);for(n in a){l=a[n].right;g=a[n].left;f=a[n].index;h=f-1;m=void 0!==g?g:l;0>h&&(h=0);c=m.geometry;d=e=c.Z();p=!1;switch(c.X()){case "MultiLineString":2<e[m.depth[0]].length&&(e[m.depth[0]].splice(f, +1),p=!0);break;case "LineString":2<e.length&&(e.splice(f,1),p=!0);break;case "MultiPolygon":d=d[m.depth[1]];case "Polygon":d=d[m.depth[0]],4<d.length&&(f==d.length-1&&(f=0),d.splice(f,1),p=!0,0===f&&(d.pop(),d.push(d[0]),h=d.length-1))}p&&(d=c,this.s=!0,d.pa(e),this.s=!1,e=[],void 0!==g&&(this.a.remove(g),e.push(g.na[0])),void 0!==l&&(this.a.remove(l),e.push(l.na[1])),void 0!==g&&void 0!==l&&(g={depth:m.depth,feature:m.feature,geometry:m.geometry,index:h,na:e},this.a.Ca(Kb(g.na),g)),Uu(this,c,f,m.depth, +-1),this.c&&(this.S.ha().nb(this.c),this.c=null))}a=!0;this.b(new Hu("modifyend",this.A,b));this.D=!1}return a};function Uu(a,b,c,d,e){ql(a.a,b.H(),function(a){a.geometry===b&&(void 0===d||void 0===a.depth||pb(a.depth,d))&&a.index>c&&(a.index+=e)})}function Nu(){var a=wj();return function(){return a.Point}};function Vu(a,b,c,d){Wa.call(this,a);this.selected=b;this.deselected=c;this.mapBrowserEvent=d}y(Vu,Wa); +function Wu(a){Vh.call(this,{handleEvent:Xu});var b=a?a:{};this.C=b.condition?b.condition:di;this.A=b.addCondition?b.addCondition:rc;this.D=b.removeCondition?b.removeCondition:rc;this.R=b.toggleCondition?b.toggleCondition:fi;this.j=b.multi?b.multi:!1;this.o=b.filter?b.filter:qc;this.c=new G({source:new P({useSpatialIndex:!1,features:b.features,wrapX:b.wrapX}),style:b.style?b.style:Yu(),updateWhileAnimating:!0,updateWhileInteracting:!0});if(b.layers)if("function"===typeof b.layers)a=function(a){return b.layers(a)}; +else{var c=b.layers;a=function(a){return jb(c,a)}}else a=qc;this.s=a;this.a={};a=this.c.ha().c;B(a,"add",this.Am,this);B(a,"remove",this.Dm,this)}y(Wu,Vh);k=Wu.prototype;k.Bm=function(){return this.c.ha().c};k.Cm=function(a){a=w(a);return this.a[a]}; +function Xu(a){if(!this.C(a))return!0;var b=this.A(a),c=this.D(a),d=this.R(a),e=!b&&!c&&!d,f=a.map,g=this.c.ha().c,h=[],l=[];if(e)Fa(this.a),f.kd(a.pixel,function(a,b){if(this.o(a,b)){l.push(a);var c=w(a);this.a[c]=b;return!this.j}},this,this.s),0<l.length&&1==g.dc()&&g.item(0)==l[0]?l.length=0:(0!==g.dc()&&(h=Array.prototype.concat(g.a),g.clear()),g.qf(l));else{f.kd(a.pixel,function(a,e){if(this.o(a,e)){if(!b&&!d||jb(g.a,a))(c||d)&&jb(g.a,a)&&(h.push(a),f=w(a),delete this.a[f]);else{l.push(a);var f= +w(a);this.a[f]=e}return!this.j}},this,this.s);for(e=h.length-1;0<=e;--e)g.remove(h[e]);g.qf(l)}(0<l.length||0<h.length)&&this.b(new Vu("select",l,h,a));return ci(a)}k.setMap=function(a){var b=this.v,c=this.c.ha().c;b&&c.forEach(b.wi,b);Vh.prototype.setMap.call(this,a);this.c.setMap(a);a&&c.forEach(a.ti,a)};function Yu(){var a=wj();mb(a.Polygon,a.LineString);mb(a.GeometryCollection,a.LineString);return function(b){return a[b.W().X()]}}k.Am=function(a){a=a.element;var b=this.v;b&&b.ti(a)}; +k.Dm=function(a){a=a.element;var b=this.v;b&&b.wi(a)};function Zu(a){ji.call(this,{handleEvent:$u,handleDownEvent:qc,handleUpEvent:av});a=a?a:{};this.s=a.source?a.source:null;this.qa=void 0!==a.vertex?a.vertex:!0;this.T=void 0!==a.edge?a.edge:!0;this.j=a.features?a.features:null;this.ta=[];this.D={};this.R={};this.Y={};this.A={};this.S=null;this.c=void 0!==a.pixelTolerance?a.pixelTolerance:10;this.Aa=bv.bind(this);this.a=new kl;this.za={Point:this.Jm,LineString:this.nh,LinearRing:this.nh,Polygon:this.Km,MultiPoint:this.Hm,MultiLineString:this.Gm,MultiPolygon:this.Im, +GeometryCollection:this.Fm}}y(Zu,ji);k=Zu.prototype;k.rb=function(a,b){var c=void 0!==b?b:!0,d=w(a),e=a.W();if(e){var f=this.za[e.X()];f&&(this.Y[d]=e.H(Lb()),f.call(this,a,e),c&&(this.R[d]=B(e,"change",this.Kk.bind(this,a),this)))}c&&(this.D[d]=B(a,gb(a.a),this.Em,this))};k.Jj=function(a){this.rb(a)};k.Kj=function(a){this.nb(a)};k.lh=function(a){var b;a instanceof vl?b=a.feature:a instanceof ke&&(b=a.element);this.rb(b)}; +k.mh=function(a){var b;a instanceof vl?b=a.feature:a instanceof ke&&(b=a.element);this.nb(b)};k.Em=function(a){a=a.target;this.nb(a,!0);this.rb(a,!0)};k.Kk=function(a){if(this.C){var b=w(a);b in this.A||(this.A[b]=a)}else this.xi(a)};k.nb=function(a,b){var c=void 0!==b?b:!0,d=w(a),e=this.Y[d];if(e){var f=this.a,g=[];ql(f,e,function(b){a===b.feature&&g.push(b)});for(e=g.length-1;0<=e;--e)f.remove(g[e]);c&&(cb(this.R[d]),delete this.R[d])}c&&(cb(this.D[d]),delete this.D[d])}; +k.setMap=function(a){var b=this.v,c=this.ta,d;this.j?d=this.j:this.s&&(d=this.s.oe());b&&(c.forEach(cb),c.length=0,d.forEach(this.Kj,this));ji.prototype.setMap.call(this,a);a&&(this.j?c.push(B(this.j,"add",this.lh,this),B(this.j,"remove",this.mh,this)):this.s&&c.push(B(this.s,"addfeature",this.lh,this),B(this.s,"removefeature",this.mh,this)),d.forEach(this.Jj,this))};k.Gc=rc;k.xi=function(a){this.nb(a,!1);this.rb(a,!1)}; +k.Fm=function(a,b){var c,d=b.c;for(c=0;c<d.length;++c)this.za[d[c].X()].call(this,a,d[c])};k.nh=function(a,b){var c=b.Z(),d,e,f,g;d=0;for(e=c.length-1;d<e;++d)f=c.slice(d,d+2),g={feature:a,na:f},this.a.Ca(Kb(f),g)};k.Gm=function(a,b){var c=b.Z(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,na:l},this.a.Ca(Kb(l),m)};k.Hm=function(a,b){var c=b.Z(),d,e,f;e=0;for(f=c.length;e<f;++e)d=c[e],d={feature:a,na:[d,d]},this.a.Ca(b.H(),d)}; +k.Im=function(a,b){var c=b.Z(),d,e,f,g,h,l,m,n,p,q;l=0;for(m=c.length;l<m;++l)for(n=c[l],g=0,h=n.length;g<h;++g)for(d=n[g],e=0,f=d.length-1;e<f;++e)p=d.slice(e,e+2),q={feature:a,na:p},this.a.Ca(Kb(p),q)};k.Jm=function(a,b){var c=b.Z(),c={feature:a,na:[c,c]};this.a.Ca(b.H(),c)};k.Km=function(a,b){var c=b.Z(),d,e,f,g,h,l,m;g=0;for(h=c.length;g<h;++g)for(d=c[g],e=0,f=d.length-1;e<f;++e)l=d.slice(e,e+2),m={feature:a,na:l},this.a.Ca(Kb(l),m)}; +function $u(a){var b,c,d=a.pixel,e=a.coordinate;b=a.map;var f=b.Ma([d[0]-this.c,d[1]+this.c]);c=b.Ma([d[0]+this.c,d[1]-this.c]);var f=Kb([f,c]),g=nl(this.a,f),h,f=!1,l=null;c=null;if(0<g.length){this.S=e;g.sort(this.Aa);g=g[0].na;if(this.qa&&!this.T){if(e=b.Ga(g[0]),h=b.Ga(g[1]),e=Hb(d,e),d=Hb(d,h),h=Math.sqrt(Math.min(e,d)),h=h<=this.c)f=!0,l=e>d?g[1]:g[0],c=b.Ga(l)}else this.T&&(l=Cb(e,g),c=b.Ga(l),Math.sqrt(Hb(d,c))<=this.c&&(f=!0,this.qa&&(e=b.Ga(g[0]),h=b.Ga(g[1]),e=Hb(c,e),d=Hb(c,h),h=Math.sqrt(Math.min(e, +d)),h=h<=this.c)))&&(l=e>d?g[1]:g[0],c=b.Ga(l));f&&(c=[Math.round(c[0]),Math.round(c[1])])}b=l;f&&(a.coordinate=b.slice(0,2),a.pixel=c);return ki.call(this,a)}function av(){var a=Ga(this.A);a.length&&(a.forEach(this.xi,this),this.A={});return!1}function bv(a,b){return Ib(this.S,a.na)-Ib(this.S,b.na)};function cv(a,b,c){Wa.call(this,a);this.features=b;this.coordinate=c}y(cv,Wa);function dv(a){ji.call(this,{handleDownEvent:ev,handleDragEvent:fv,handleMoveEvent:gv,handleUpEvent:hv});this.s=void 0;this.a=null;this.c=void 0!==a.features?a.features:null;var b;if(a.layers)if("function"===typeof a.layers)b=function(b){return a.layers(b)};else{var c=a.layers;b=function(a){return jb(c,a)}}else b=qc;this.A=b;this.j=null}y(dv,ji); +function ev(a){this.j=iv(this,a.pixel,a.map);return!this.a&&this.j?(this.a=a.coordinate,gv.call(this,a),this.b(new cv("translatestart",this.c,a.coordinate)),!0):!1}function hv(a){return this.a?(this.a=null,gv.call(this,a),this.b(new cv("translateend",this.c,a.coordinate)),!0):!1} +function fv(a){if(this.a){a=a.coordinate;var b=a[0]-this.a[0],c=a[1]-this.a[1];if(this.c)this.c.forEach(function(a){var d=a.W();d.Sc(b,c);a.Ua(d)});else if(this.j){var d=this.j.W();d.Sc(b,c);this.j.Ua(d)}this.a=a;this.b(new cv("translating",this.c,a))}} +function gv(a){var b=a.map.yc();if(a=a.map.kd(a.pixel,function(a){return a})){var c=!1;this.c&&jb(this.c.a,a)&&(c=!0);this.s=b.style.cursor;b.style.cursor=this.a?"-webkit-grabbing":c?"-webkit-grab":"pointer";b.style.cursor=this.a?c?"grab":"pointer":"grabbing"}else b.style.cursor=void 0!==this.s?this.s:"",this.s=void 0}function iv(a,b,c){var d=null;b=c.kd(b,function(a){return a},a,a.A);a.c&&jb(a.c.a,b)&&(d=b);return d};function V(a){a=a?a:{};var b=Ea({},a);delete b.gradient;delete b.radius;delete b.blur;delete b.shadow;delete b.weight;G.call(this,b);this.f=null;this.ia=void 0!==a.shadow?a.shadow:250;this.Y=void 0;this.c=null;B(this,gb("gradient"),this.Lk,this);this.ii(a.gradient?a.gradient:jv);this.di(void 0!==a.blur?a.blur:15);this.qh(void 0!==a.radius?a.radius:8);B(this,gb("blur"),this.lf,this);B(this,gb("radius"),this.lf,this);this.lf();var c=a.weight?a.weight:"weight",d;"string"===typeof c?d=function(a){return a.get(c)}: +d=c;this.l(function(a){a=d(a);a=void 0!==a?sa(a,0,1):1;var b=255*a|0,c=this.c[b];c||(c=[new rj({image:new Dh({opacity:a,src:this.Y})})],this.c[b]=c);return c}.bind(this));this.set("renderOrder",null);B(this,"render",this.dl,this)}y(V,G);var jv=["#00f","#0ff","#0f0","#ff0","#f00"];k=V.prototype;k.zg=function(){return this.get("blur")};k.Gg=function(){return this.get("gradient")};k.ph=function(){return this.get("radius")}; +k.Lk=function(){for(var a=this.Gg(),b=Oe(1,256),c=b.createLinearGradient(0,0,1,256),d=1/(a.length-1),e=0,f=a.length;e<f;++e)c.addColorStop(e*d,a[e]);b.fillStyle=c;b.fillRect(0,0,1,256);this.f=b.getImageData(0,0,1,256).data};k.lf=function(){var a=this.ph(),b=this.zg(),c=a+b+1,d=2*c,d=Oe(d,d);d.shadowOffsetX=d.shadowOffsetY=this.ia;d.shadowBlur=b;d.shadowColor="#000";d.beginPath();b=c-this.ia;d.arc(b,b,a,0,2*Math.PI,!0);d.fill();this.Y=d.canvas.toDataURL();this.c=Array(256);this.u()}; +k.dl=function(a){a=a.context;var b=a.canvas,b=a.getImageData(0,0,b.width,b.height),c=b.data,d,e,f;d=0;for(e=c.length;d<e;d+=4)if(f=4*c[d+3])c[d]=this.f[f],c[d+1]=this.f[f+1],c[d+2]=this.f[f+2];a.putImageData(b,0,0)};k.di=function(a){this.set("blur",a)};k.ii=function(a){this.set("gradient",a)};k.qh=function(a){this.set("radius",a)};function kv(a,b,c,d){function e(){delete pa[g];f.parentNode.removeChild(f)}var f=pa.document.createElement("script"),g="olc_"+w(b);f.async=!0;f.src=a+(-1==a.indexOf("?")?"?":"&")+(d||"callback")+"="+g;var h=pa.setTimeout(function(){e();c&&c()},1E4);pa[g]=function(a){pa.clearTimeout(h);e();b(a)};pa.document.getElementsByTagName("head")[0].appendChild(f)};function lv(a,b,c,d,e,f,g,h,l,m,n){df.call(this,e,0);this.R=void 0!==n?n:!1;this.D=g;this.C=h;this.l=null;this.c={};this.j=b;this.v=d;this.U=f?f:e;this.g=[];this.Wc=null;this.s=0;f=d.Ea(this.U);h=this.v.H();e=this.j.H();f=h?mc(f,h):f;if(0===gc(f))this.state=4;else if((h=a.H())&&(e?e=mc(e,h):e=h),d=d.$(this.U[0]),d=tk(a,c,kc(f),d),!isFinite(d)||0>=d)this.state=4;else if(this.A=new wk(a,c,f,e,d*(void 0!==m?m:.5)),0===this.A.f.length)this.state=4;else if(this.s=b.Lb(d),c=yk(this.A),e&&(a.a?(c[1]=sa(c[1], +e[1],e[3]),c[3]=sa(c[3],e[1],e[3])):c=mc(c,e)),gc(c))if(a=pf(b,c,this.s),100>(a.ea-a.ca+1)*(a.ga-a.fa+1)){for(b=a.ca;b<=a.ea;b++)for(c=a.fa;c<=a.ga;c++)(m=l(this.s,b,c,g))&&this.g.push(m);0===this.g.length&&(this.state=4)}else this.state=3;else this.state=4}y(lv,df);lv.prototype.ka=function(){1==this.state&&(this.Wc.forEach(Ka),this.Wc=null);df.prototype.ka.call(this)}; +lv.prototype.$a=function(a){if(void 0!==a){var b=w(a);if(b in this.c)return this.c[b];a=Ha(this.c)?this.l:this.l.cloneNode(!1);return this.c[b]=a}return this.l}; +lv.prototype.zd=function(){var a=[];this.g.forEach(function(b){b&&2==b.V()&&a.push({extent:this.j.Ea(b.ma),image:b.$a()})},this);this.g.length=0;if(0===a.length)this.state=3;else{var b=this.U[0],c=this.v.Ja(b),d=ea(c)?c:c[0],c=ea(c)?c:c[1],b=this.v.$(b),e=this.j.$(this.s),f=this.v.Ea(this.U);this.l=vk(d,c,this.D,e,this.j.H(),b,f,this.A,a,this.C,this.R);this.state=2}ef(this)}; +lv.prototype.load=function(){if(0==this.state){this.state=1;ef(this);var a=0;this.Wc=[];this.g.forEach(function(b){var c=b.V();if(0==c||1==c){a++;var d;d=B(b,"change",function(){var c=b.V();if(2==c||3==c||4==c)Ka(d),a--,0===a&&(this.Wc.forEach(Ka),this.Wc=null,this.zd())},this);this.Wc.push(d)}},this);this.g.forEach(function(a){0==a.V()&&a.load()});0===a&&pa.setTimeout(this.zd.bind(this),0)}};function W(a){Jl.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,extent:a.extent,logo:a.logo,opaque:a.opaque,projection:a.projection,state:a.state,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction?a.tileLoadFunction:mv,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:a.tileUrlFunction,url:a.url,urls:a.urls,wrapX:a.wrapX});this.crossOrigin=void 0!==a.crossOrigin?a.crossOrigin:null;this.tileClass=void 0!==a.tileClass?a.tileClass:fu;this.i={};this.s={};this.qa=a.reprojectionErrorThreshold; +this.C=!1}y(W,Jl);k=W.prototype;k.Ah=function(){if(cf(this.a))return!0;for(var a in this.i)if(cf(this.i[a]))return!0;return!1};k.Lc=function(a,b){var c=this.pd(a);this.a.Lc(this.a==c?b:{});for(var d in this.i){var e=this.i[d];e.Lc(e==c?b:{})}};k.Ud=function(a){return this.f&&a&&!Oc(this.f,a)?0:this.gf()};k.gf=function(){return 0};k.jf=function(a){return this.f&&a&&!Oc(this.f,a)?!1:Jl.prototype.jf.call(this,a)}; +k.eb=function(a){var b=this.f;return!this.tileGrid||b&&!Oc(b,a)?(b=w(a).toString(),b in this.s||(this.s[b]=vf(a)),this.s[b]):this.tileGrid};k.pd=function(a){var b=this.f;if(!b||Oc(b,a))return this.a;a=w(a).toString();a in this.i||(this.i[a]=new bf);return this.i[a]};function nv(a,b,c,d,e,f,g){b=[b,c,d];e=(c=Cf(a,b,f))?a.tileUrlFunction(c,e,f):void 0;e=new a.tileClass(b,void 0!==e?0:4,void 0!==e?e:"",a.crossOrigin,a.tileLoadFunction);e.key=g;B(e,"change",a.Bh,a);return e} +k.ac=function(a,b,c,d,e){if(this.f&&e&&!Oc(this.f,e)){var f=this.pd(e);b=[a,b,c];a=this.Eb.apply(this,b);if(Ze(f,a))return f.get(a);var g=this.f;c=this.eb(g);var h=this.eb(e),l=Cf(this,b,e);d=new lv(g,c,e,h,b,l,this.bc(d),this.gf(),function(a,b,c,d){return ov(this,a,b,c,d,g)}.bind(this),this.qa,this.C);f.set(a,d);return d}return ov(this,a,b,c,d,e)}; +function ov(a,b,c,d,e,f){var g,h=a.Eb(b,c,d),l=a.cc;if(Ze(a.a,h)){if(g=a.a.get(h),g.key!=l){var m=g;g.a&&g.a.key==l?(g=g.a,2==m.V()&&(g.a=m)):(g=nv(a,b,c,d,e,f,l),2==m.V()?g.a=m:m.a&&2==m.a.V()&&(g.a=m.a,m.a=null));g.a&&(g.a.a=null);a.a.replace(h,g)}}else g=nv(a,b,c,d,e,f,l),a.a.set(h,g);return g}k.zb=function(a){if(this.C!=a){this.C=a;for(var b in this.i)this.i[b].clear();this.u()}};k.Ab=function(a,b){var c=yc(a);c&&(c=w(c).toString(),c in this.s||(this.s[c]=b))};function mv(a,b){a.$a().src=b};function pv(a){W.call(this,{cacheSize:a.cacheSize,crossOrigin:"anonymous",opaque:!0,projection:yc("EPSG:3857"),reprojectionErrorThreshold:a.reprojectionErrorThreshold,state:"loading",tileLoadFunction:a.tileLoadFunction,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.j=void 0!==a.culture?a.culture:"en-us";this.c=void 0!==a.maxZoom?a.maxZoom:-1;kv("https://dev.virtualearth.net/REST/v1/Imagery/Metadata/"+a.imagerySet+"?uriScheme=https&include=ImageryProviders&key="+a.key,this.v.bind(this),void 0,"jsonp")} +y(pv,W);var qv=new je({html:'<a class="ol-attribution-bing-tos" href="http://www.microsoft.com/maps/product/terms.html">Terms of Use</a>'}); +pv.prototype.v=function(a){if(200!=a.statusCode||"OK"!=a.statusDescription||"ValidCredentials"!=a.authenticationResultCode||1!=a.resourceSets.length||1!=a.resourceSets[0].resources.length)lf(this,"error");else{var b=a.brandLogoUri;-1==b.indexOf("https")&&(b=b.replace("http","https"));var c=a.resourceSets[0].resources[0],d=-1==this.c?c.zoomMax:this.c;a=wf(this.f);var e=yf({extent:a,minZoom:c.zoomMin,maxZoom:d,tileSize:c.imageWidth==c.imageHeight?c.imageWidth:[c.imageWidth,c.imageHeight]});this.tileGrid= +e;var f=this.j;this.tileUrlFunction=Gl(c.imageUrlSubdomains.map(function(a){var b=[0,0,0],d=c.imageUrl.replace("{subdomain}",a).replace("{culture}",f);return function(a){if(a)return $e(a[0],a[1],-a[2]-1,b),d.replace("{quadkey}",af(b))}}));if(c.imageryProviders){var g=Bc(yc("EPSG:4326"),this.f);a=c.imageryProviders.map(function(a){var b=a.attribution,c={};a.coverageAreas.forEach(function(a){var b=a.zoomMin,f=Math.min(a.zoomMax,d);a=a.bbox;a=pc([a[1],a[0],a[3],a[2]],g);var h,l;for(h=b;h<=f;++h)l=h.toString(), +b=pf(e,a,h),l in c?c[l].push(b):c[l]=[b]});return new je({html:b,tileRanges:c})});a.push(qv);this.oa(a)}this.R=b;lf(this,"ready")}};function rv(a){a=a||{};var b=void 0!==a.projection?a.projection:"EPSG:3857",c=void 0!==a.tileGrid?a.tileGrid:yf({extent:wf(b),maxZoom:a.maxZoom,minZoom:a.minZoom,tileSize:a.tileSize});W.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,opaque:a.opaque,projection:b,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:c,tileLoadFunction:a.tileLoadFunction,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:a.tileUrlFunction,url:a.url,urls:a.urls, +wrapX:void 0!==a.wrapX?a.wrapX:!0})}y(rv,W);function sv(a){this.v=a.account;this.A=a.map||"";this.c=a.config||{};this.j={};rv.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,maxZoom:void 0!==a.maxZoom?a.maxZoom:18,minZoom:a.minZoom,projection:a.projection,state:"loading",wrapX:a.wrapX});tv(this)}y(sv,rv);k=sv.prototype;k.Tj=function(){return this.c};k.up=function(a){Ea(this.c,a);tv(this)};k.$o=function(a){this.c=a||{};tv(this)}; +function tv(a){var b=JSON.stringify(a.c);if(a.j[b])uv(a,a.j[b]);else{var c="https://"+a.v+".cartodb.com/api/v1/map";a.A&&(c+="/named/"+a.A);var d=new XMLHttpRequest;d.addEventListener("load",a.Nk.bind(a,b));d.addEventListener("error",a.Mk.bind(a));d.open("POST",c);d.setRequestHeader("Content-type","application/json");d.send(JSON.stringify(a.c))}} +k.Nk=function(a,b){var c=b.target;if(200<=c.status&&300>c.status){var d;try{d=JSON.parse(c.responseText)}catch(e){lf(this,"error");return}uv(this,d);this.j[a]=d;lf(this,"ready")}else lf(this,"error")};k.Mk=function(){lf(this,"error")};function uv(a,b){a.Va("https://"+b.cdn_url.https+"/"+a.v+"/api/v1/map/"+b.layergroupid+"/{z}/{x}/{y}.png")};function Y(a){P.call(this,{attributions:a.attributions,extent:a.extent,logo:a.logo,projection:a.projection,wrapX:a.wrapX});this.C=void 0;this.ta=void 0!==a.distance?a.distance:20;this.A=[];this.ia=a.geometryFunction||function(a){return a.W()};this.v=a.source;this.v.I("change",Y.prototype.Sa,this)}y(Y,P);Y.prototype.Aa=function(){return this.v};Y.prototype.Pc=function(a,b,c){this.v.Pc(a,b,c);b!==this.C&&(this.clear(),this.C=b,vv(this),this.Jc(this.A))}; +Y.prototype.Sa=function(){this.clear();vv(this);this.Jc(this.A);this.u()};function vv(a){if(void 0!==a.C){a.A.length=0;for(var b=Lb(),c=a.ta*a.C,d=a.v.oe(),e={},f=0,g=d.length;f<g;f++){var h=d[f];w(h).toString()in e||!(h=a.ia(h))||(h=h.Z(),Xb(h,b),Ob(b,c,b),h=a.v.ef(b),h=h.filter(function(a){a=w(a).toString();return a in e?!1:e[a]=!0}),a.A.push(wv(a,h)))}}} +function wv(a,b){for(var c=[0,0],d=b.length-1;0<=d;--d){var e=a.ia(b[d]);e?Bb(c,e.Z()):b.splice(d,1)}d=1/b.length;c[0]*=d;c[1]*=d;c=new Ik(new C(c));c.set("features",b);return c};function xv(a,b){var c=Object.keys(b).map(function(a){return a+"="+encodeURIComponent(b[a])}).join("&");a=a.replace(/[?&]$/,"");a=-1===a.indexOf("?")?a+"?":a+"&";return a+c};function yv(a){a=a||{};Ak.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions});this.Y=void 0!==a.crossOrigin?a.crossOrigin:null;this.i=a.url;this.j=void 0!==a.imageLoadFunction?a.imageLoadFunction:Gk;this.v=a.params||{};this.c=null;this.s=[0,0];this.T=0;this.S=void 0!==a.ratio?a.ratio:1.5}y(yv,Ak);k=yv.prototype;k.Sm=function(){return this.v}; +k.Mc=function(a,b,c,d){if(void 0===this.i)return null;b=Bk(this,b);var e=this.c;if(e&&this.T==this.g&&e.$()==b&&e.f==c&&Ub(e.H(),a))return e;e={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};Ea(e,this.v);a=a.slice();var f=(a[0]+a[2])/2,g=(a[1]+a[3])/2;if(1!=this.S){var h=this.S*ic(a)/2,l=this.S*jc(a)/2;a[0]=f-h;a[1]=g-l;a[2]=f+h;a[3]=g+l}var h=b/c,l=Math.ceil(ic(a)/h),m=Math.ceil(jc(a)/h);a[0]=f-h*l/2;a[2]=f+h*l/2;a[1]=g-h*m/2;a[3]=g+h*m/2;this.s[0]=l;this.s[1]=m;f=a;g=this.s;d=d.cb.split(":").pop();e.SIZE= +g[0]+","+g[1];e.BBOX=f.join(",");e.BBOXSR=d;e.IMAGESR=d;e.DPI=90*c;d=this.i.replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage");e=xv(d,e);this.c=new eu(a,b,c,this.l,e,this.Y,this.j);this.T=this.g;B(this.c,"change",this.o,this);return this.c};k.Rm=function(){return this.j};k.Tm=function(){return this.i};k.Um=function(a){this.c=null;this.j=a;this.u()};k.Vm=function(a){a!=this.i&&(this.i=a,this.c=null,this.u())};k.Wm=function(a){Ea(this.v,a);this.c=null;this.u()};function zv(a){Ak.call(this,{projection:a.projection,resolutions:a.resolutions});this.Y=void 0!==a.crossOrigin?a.crossOrigin:null;this.s=void 0!==a.displayDpi?a.displayDpi:96;this.j=a.params||{};this.T=a.url;this.c=void 0!==a.imageLoadFunction?a.imageLoadFunction:Gk;this.ia=void 0!==a.hidpi?a.hidpi:!0;this.ta=void 0!==a.metersPerUnit?a.metersPerUnit:1;this.v=void 0!==a.ratio?a.ratio:1;this.Aa=void 0!==a.useOverlay?a.useOverlay:!1;this.i=null;this.S=0}y(zv,Ak);k=zv.prototype;k.Ym=function(){return this.j}; +k.Mc=function(a,b,c){b=Bk(this,b);c=this.ia?c:1;var d=this.i;if(d&&this.S==this.g&&d.$()==b&&d.f==c&&Ub(d.H(),a))return d;1!=this.v&&(a=a.slice(),oc(a,this.v));var e=[ic(a)/b*c,jc(a)/b*c];if(void 0!==this.T){var d=this.T,f=kc(a),g=this.ta,h=ic(a),l=jc(a),m=e[0],n=e[1],p=.0254/this.s,e={OPERATION:this.Aa?"GETDYNAMICMAPOVERLAYIMAGE":"GETMAPIMAGE",VERSION:"2.0.0",LOCALE:"en",CLIENTAGENT:"ol.source.ImageMapGuide source",CLIP:"1",SETDISPLAYDPI:this.s,SETDISPLAYWIDTH:Math.round(e[0]),SETDISPLAYHEIGHT:Math.round(e[1]), +SETVIEWSCALE:n*h>m*l?h*g/(m*p):l*g/(n*p),SETVIEWCENTERX:f[0],SETVIEWCENTERY:f[1]};Ea(e,this.j);d=xv(d,e);d=new eu(a,b,c,this.l,d,this.Y,this.c);B(d,"change",this.o,this)}else d=null;this.i=d;this.S=this.g;return d};k.Xm=function(){return this.c};k.$m=function(a){Ea(this.j,a);this.u()};k.Zm=function(a){this.i=null;this.c=a;this.u()};function Av(a){var b=a.imageExtent,c=void 0!==a.crossOrigin?a.crossOrigin:null,d=void 0!==a.imageLoadFunction?a.imageLoadFunction:Gk;Ak.call(this,{attributions:a.attributions,logo:a.logo,projection:yc(a.projection)});this.c=new eu(b,void 0,1,this.l,a.url,c,d);this.i=a.imageSize?a.imageSize:null;B(this.c,"change",this.o,this)}y(Av,Ak);Av.prototype.Mc=function(a){return nc(a,this.c.H())?this.c:null}; +Av.prototype.o=function(a){if(2==this.c.V()){var b=this.c.H(),c=this.c.a(),d,e;this.i?(d=this.i[0],e=this.i[1]):(d=c.width,e=c.height);b=Math.ceil(ic(b)/(jc(b)/e));if(b!=d){var b=Oe(b,e),f=b.canvas;b.drawImage(c,0,0,d,e,0,0,f.width,f.height);this.c.g=f}}Ak.prototype.o.call(this,a)};function Bv(a){a=a||{};Ak.call(this,{attributions:a.attributions,logo:a.logo,projection:a.projection,resolutions:a.resolutions});this.ta=void 0!==a.crossOrigin?a.crossOrigin:null;this.j=a.url;this.S=void 0!==a.imageLoadFunction?a.imageLoadFunction:Gk;this.i=a.params||{};this.v=!0;Cv(this);this.ia=a.serverType;this.Aa=void 0!==a.hidpi?a.hidpi:!0;this.c=null;this.T=[0,0];this.Y=0;this.s=void 0!==a.ratio?a.ratio:1.5}y(Bv,Ak);var Dv=[101,101];k=Bv.prototype; +k.fn=function(a,b,c,d){if(void 0!==this.j){var e=lc(a,b,0,Dv),f={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.i.LAYERS};Ea(f,this.i,d);d=Math.floor((e[3]-a[1])/b);f[this.v?"I":"X"]=Math.floor((a[0]-e[0])/b);f[this.v?"J":"Y"]=d;return Ev(this,e,Dv,1,yc(c),f)}};k.hn=function(){return this.i}; +k.Mc=function(a,b,c,d){if(void 0===this.j)return null;b=Bk(this,b);1==c||this.Aa&&void 0!==this.ia||(c=1);a=a.slice();var e=(a[0]+a[2])/2,f=(a[1]+a[3])/2,g=b/c,h=ic(a)/g,g=jc(a)/g,l=this.c;if(l&&this.Y==this.g&&l.$()==b&&l.f==c&&Ub(l.H(),a))return l;if(1!=this.s){var l=this.s*ic(a)/2,m=this.s*jc(a)/2;a[0]=e-l;a[1]=f-m;a[2]=e+l;a[3]=f+m}e={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};Ea(e,this.i);this.T[0]=Math.ceil(h*this.s);this.T[1]=Math.ceil(g*this.s);d=Ev(this, +a,this.T,c,d,e);this.c=new eu(a,b,c,this.l,d,this.ta,this.S);this.Y=this.g;B(this.c,"change",this.o,this);return this.c};k.gn=function(){return this.S}; +function Ev(a,b,c,d,e,f){f[a.v?"CRS":"SRS"]=e.cb;"STYLES"in a.i||(f.STYLES="");if(1!=d)switch(a.ia){case "geoserver":d=90*d+.5|0;f.FORMAT_OPTIONS="FORMAT_OPTIONS"in f?f.FORMAT_OPTIONS+(";dpi:"+d):"dpi:"+d;break;case "mapserver":f.MAP_RESOLUTION=90*d;break;case "carmentaserver":case "qgis":f.DPI=90*d}f.WIDTH=c[0];f.HEIGHT=c[1];c=e.b;var g;a.v&&"ne"==c.substr(0,2)?g=[b[1],b[0],b[3],b[2]]:g=b;f.BBOX=g.join(",");return xv(a.j,f)}k.jn=function(){return this.j};k.kn=function(a){this.c=null;this.S=a;this.u()}; +k.ln=function(a){a!=this.j&&(this.j=a,this.c=null,this.u())};k.mn=function(a){Ea(this.i,a);Cv(this);this.c=null;this.u()};function Cv(a){a.v=0<=Ab(a.i.VERSION||"1.3.0")};function Fv(a){a=a||{};var b;void 0!==a.attributions?b=a.attributions:b=[Gv];rv.call(this,{attributions:b,cacheSize:a.cacheSize,crossOrigin:void 0!==a.crossOrigin?a.crossOrigin:"anonymous",opaque:void 0!==a.opaque?a.opaque:!0,maxZoom:void 0!==a.maxZoom?a.maxZoom:19,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileLoadFunction:a.tileLoadFunction,url:void 0!==a.url?a.url:"https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png",wrapX:a.wrapX})}y(Fv,rv);var Gv=new je({html:'© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors.'});(function(){var a={},b={ja:a};(function(c){if("object"===typeof a&&"undefined"!==typeof b)b.ja=c();else{var d;"undefined"!==typeof window?d=window:"undefined"!==typeof global?d=global:"undefined"!==typeof self?d=self:d=this;d.Sp=c()}})(function(){return function d(a,b,g){function h(m,p){if(!b[m]){if(!a[m]){var q="function"==typeof require&&require;if(!p&&q)return q(m,!0);if(l)return l(m,!0);q=Error("Cannot find module '"+m+"'");throw q.code="MODULE_NOT_FOUND",q;}q=b[m]={ja:{}};a[m][0].call(q.ja,function(b){var d= +a[m][1][b];return h(d?d:b)},q,q.ja,d,a,b,g)}return b[m].ja}for(var l="function"==typeof require&&require,m=0;m<g.length;m++)h(g[m]);return h}({1:[function(a,b,f){a=a("./processor");f.$i=a},{"./processor":2}],2:[function(a,b){function f(a){var b=!0;try{new ImageData(10,10)}catch(d){b=!1}return function(d){var e=d.buffers,f=d.meta,g=d.width,h=d.height,l=e.length,m=e[0].byteLength;if(d.imageOps){m=Array(l);for(d=0;d<l;++d){var z=m,F=d,N;N=new Uint8ClampedArray(e[d]);var K=g,X=h;N=b?new ImageData(N,K, +X):{data:N,width:K,height:X};z[F]=N}g=a(m,f).data}else{g=new Uint8ClampedArray(m);h=Array(l);z=Array(l);for(d=0;d<l;++d)h[d]=new Uint8ClampedArray(e[d]),z[d]=[0,0,0,0];for(e=0;e<m;e+=4){for(d=0;d<l;++d)F=h[d],z[d][0]=F[e],z[d][1]=F[e+1],z[d][2]=F[e+2],z[d][3]=F[e+3];d=a(z,f);g[e]=d[0];g[e+1]=d[1];g[e+2]=d[2];g[e+3]=d[3]}}return g.buffer}}function g(a,b){var d=Object.keys(a.lib||{}).map(function(b){return"var "+b+" = "+a.lib[b].toString()+";"}).concat(["var __minion__ = ("+f.toString()+")(",a.operation.toString(), +");",'self.addEventListener("message", function(event) {'," var buffer = __minion__(event.data);"," self.postMessage({buffer: buffer, meta: event.data.meta}, [buffer]);","});"]),d=URL.createObjectURL(new Blob(d,{type:"text/javascript"})),d=new Worker(d);d.addEventListener("message",b);return d}function h(a,b){var d=f(a.operation);return{postMessage:function(a){setTimeout(function(){b({data:{buffer:d(a),meta:a.meta}})},0)}}}function l(a){this.Re=!!a.ll;var b;0===a.threads?b=0:this.Re?b=1:b=a.threads|| +1;var d=[];if(b)for(var e=0;e<b;++e)d[e]=g(a,this.ig.bind(this,e));else d[0]=h(a,this.ig.bind(this,0));this.Ld=d;this.$c=[];this.oj=a.uo||Infinity;this.Jd=0;this.Ic={};this.Se=null}var m=a("./util").Fl;l.prototype.so=function(a,b,d){this.lj({Ac:a,Xg:b,qg:d});this.fg()};l.prototype.lj=function(a){for(this.$c.push(a);this.$c.length>this.oj;)this.$c.shift().qg(null,null)};l.prototype.fg=function(){if(0===this.Jd&&0<this.$c.length){var a=this.Se=this.$c.shift(),b=a.Ac[0].width,d=a.Ac[0].height,e=a.Ac.map(function(a){return a.data.buffer}), +f=this.Ld.length;this.Jd=f;if(1===f)this.Ld[0].postMessage({buffers:e,meta:a.Xg,imageOps:this.Re,width:b,height:d},e);else for(var g=4*Math.ceil(a.Ac[0].data.length/4/f),h=0;h<f;++h){for(var l=h*g,m=[],z=0,F=e.length;z<F;++z)m.push(e[h].slice(l,l+g));this.Ld[h].postMessage({buffers:m,meta:a.Xg,imageOps:this.Re,width:b,height:d},m)}}};l.prototype.ig=function(a,b){this.Pp||(this.Ic[a]=b.data,--this.Jd,0===this.Jd&&this.pj())};l.prototype.pj=function(){var a=this.Se,b=this.Ld.length,d,e;if(1===b)d=new Uint8ClampedArray(this.Ic[0].buffer), +e=this.Ic[0].meta;else{var f=a.Ac[0].data.length;d=new Uint8ClampedArray(f);e=Array(f);for(var f=4*Math.ceil(f/4/b),g=0;g<b;++g){var h=g*f;d.set(new Uint8ClampedArray(this.Ic[g].buffer),h);e[g]=this.Ic[g].meta}}this.Se=null;this.Ic={};a.qg(null,m(d,a.Ac[0].width,a.Ac[0].height),e);this.fg()};b.ja=l},{"./util":3}],3:[function(a,b,f){var g=!0;try{new ImageData(10,10)}catch(l){g=!1}var h=document.createElement("canvas").getContext("2d");f.Fl=function(a,b,d){if(g)return new ImageData(a,b,d);b=h.createImageData(b, +d);b.data.set(a);return b}},{}]},{},[1])(1)});jl=b.ja})();function Hv(a){this.S=null;this.Aa=void 0!==a.operationType?a.operationType:"pixel";this.Sa=void 0!==a.threads?a.threads:1;this.c=Iv(a.sources);for(var b=0,c=this.c.length;b<c;++b)B(this.c[b],"change",this.u,this);this.i=Oe();this.ia=new Rh(function(){return 1},this.u.bind(this));for(var b=Jv(this.c),c={},d=0,e=b.length;d<e;++d)c[w(b[d].layer)]=b[d];this.j=this.s=null;this.Y={animate:!1,attributions:{},coordinateToPixelMatrix:Xc(),extent:null,focus:null,index:0,layerStates:c,layerStatesArray:b,logos:{}, +pixelRatio:1,pixelToCoordinateMatrix:Xc(),postRenderFunctions:[],size:[0,0],skippedFeatureUids:{},tileQueue:this.ia,time:Date.now(),usedTiles:{},viewState:{rotation:0},viewHints:[],wantedTiles:{}};Ak.call(this,{});void 0!==a.operation&&this.v(a.operation,a.lib)}y(Hv,Ak);Hv.prototype.v=function(a,b){this.S=new jl.$i({operation:a,ll:"image"===this.Aa,uo:1,lib:b,threads:this.Sa});this.u()};function Kv(a,b,c){var d=a.s;return!d||a.g!==d.Xo||c!==d.resolution||!$b(b,d.extent)} +Hv.prototype.A=function(a,b,c,d){c=!0;for(var e,f=0,g=this.c.length;f<g;++f)if(e=this.c[f].a.ha(),"ready"!==e.V()){c=!1;break}if(!c)return null;a=a.slice();if(!Kv(this,a,b))return this.j;c=this.i.canvas;e=Math.round(ic(a)/b);f=Math.round(jc(a)/b);if(e!==c.width||f!==c.height)c.width=e,c.height=f;e=Ea({},this.Y);e.viewState=Ea({},e.viewState);var f=kc(a),g=Math.round(ic(a)/b),h=Math.round(jc(a)/b);e.extent=a;e.focus=kc(a);e.size[0]=g;e.size[1]=h;g=e.viewState;g.center=f;g.projection=d;g.resolution= +b;this.j=d=new nk(a,b,1,this.l,c,this.T.bind(this,e));this.s={extent:a,resolution:b,Xo:this.g};return d}; +Hv.prototype.T=function(a,b){for(var c=this.c.length,d=Array(c),e=0;e<c;++e){var f;f=this.c[e];var g=a,h=a.layerStatesArray[e];if(f.l(g,h)){var l=g.size[0],m=g.size[1];if(Lv){var n=Lv.canvas;n.width!==l||n.height!==m?Lv=Oe(l,m):Lv.clearRect(0,0,l,m)}else Lv=Oe(l,m);f.i(g,h,Lv);f=Lv.getImageData(0,0,l,m)}else f=null;if(f)d[e]=f;else return}c={};this.b(new Mv(Nv,a,c));this.S.so(d,c,this.ta.bind(this,a,b));Sh(a.tileQueue,16,16)}; +Hv.prototype.ta=function(a,b,c,d,e){c?b(c):d&&(this.b(new Mv(Ov,a,e)),Kv(this,a.extent,a.viewState.resolution/a.pixelRatio)||this.i.putImageData(d,0,0),b(null))};var Lv=null;function Jv(a){return a.map(function(a){return jh(a.a)})}function Iv(a){for(var b=a.length,c=Array(b),d=0;d<b;++d){var e=d,f=a[d],g=null;f instanceof zf?(f=new dj({source:f}),g=new Bl(f)):f instanceof Ak&&(f=new cj({source:f}),g=new Al(f));c[e]=g}return c} +function Mv(a,b,c){Wa.call(this,a);this.extent=b.extent;this.resolution=b.viewState.resolution/b.pixelRatio;this.data=c}y(Mv,Wa);var Nv="beforeoperations",Ov="afteroperations";var Pv={terrain:{tb:"jpg",opaque:!0},"terrain-background":{tb:"jpg",opaque:!0},"terrain-labels":{tb:"png",opaque:!1},"terrain-lines":{tb:"png",opaque:!1},"toner-background":{tb:"png",opaque:!0},toner:{tb:"png",opaque:!0},"toner-hybrid":{tb:"png",opaque:!1},"toner-labels":{tb:"png",opaque:!1},"toner-lines":{tb:"png",opaque:!1},"toner-lite":{tb:"png",opaque:!0},watercolor:{tb:"jpg",opaque:!0}},Qv={terrain:{minZoom:4,maxZoom:18},toner:{minZoom:0,maxZoom:20},watercolor:{minZoom:1,maxZoom:16}}; +function Rv(a){var b=a.layer.indexOf("-"),b=-1==b?a.layer:a.layer.slice(0,b),b=Qv[b],c=Pv[a.layer];rv.call(this,{attributions:Sv,cacheSize:a.cacheSize,crossOrigin:"anonymous",maxZoom:void 0!=a.maxZoom?a.maxZoom:b.maxZoom,minZoom:void 0!=a.minZoom?a.minZoom:b.minZoom,opaque:c.opaque,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileLoadFunction:a.tileLoadFunction,url:void 0!==a.url?a.url:"https://stamen-tiles-{a-d}.a.ssl.fastly.net/"+a.layer+"/{z}/{x}/{y}."+c.tb})}y(Rv,rv); +var Sv=[new je({html:'Map tiles by <a href="http://stamen.com/">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.'}),Gv];function Tv(a){a=a||{};W.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction,url:a.url,urls:a.urls,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.c=a.params||{};this.j=Lb()}y(Tv,W);Tv.prototype.v=function(){return this.c};Tv.prototype.bc=function(a){return a}; +Tv.prototype.vc=function(a,b,c){var d=this.tileGrid;d||(d=this.eb(c));if(!(d.b.length<=a[0])){var e=d.Ea(a,this.j),f=hf(d.Ja(a[0]),this.o);1!=b&&(f=gf(f,b,this.o));d={F:"image",FORMAT:"PNG32",TRANSPARENT:!0};Ea(d,this.c);var g=this.urls;g?(c=c.cb.split(":").pop(),d.SIZE=f[0]+","+f[1],d.BBOX=e.join(","),d.BBOXSR=c,d.IMAGESR=c,d.DPI=Math.round(d.DPI?d.DPI*b:90*b),a=(1==g.length?g[0]:g[xa((a[1]<<a[0])+a[2],g.length)]).replace(/MapServer\/?$/,"MapServer/export").replace(/ImageServer\/?$/,"ImageServer/exportImage"), +a=xv(a,d)):a=void 0;return a}};Tv.prototype.A=function(a){Ea(this.c,a);this.u()};function Uv(a,b,c){df.call(this,a,2);this.l=b;this.c=c;this.g={}}y(Uv,df);Uv.prototype.$a=function(a){a=void 0!==a?w(a):-1;if(a in this.g)return this.g[a];var b=this.l,c=Oe(b[0],b[1]);c.strokeStyle="black";c.strokeRect(.5,.5,b[0]+.5,b[1]+.5);c.fillStyle="black";c.textAlign="center";c.textBaseline="middle";c.font="24px sans-serif";c.fillText(this.c,b[0]/2,b[1]/2);return this.g[a]=c.canvas}; +function Vv(a){zf.call(this,{opaque:!1,projection:a.projection,tileGrid:a.tileGrid,wrapX:void 0!==a.wrapX?a.wrapX:!0})}y(Vv,zf);Vv.prototype.ac=function(a,b,c){var d=this.Eb(a,b,c);if(Ze(this.a,d))return this.a.get(d);var e=hf(this.tileGrid.Ja(a));a=[a,b,c];b=(b=Cf(this,a))?Cf(this,b).toString():"";e=new Uv(a,e,b);this.a.set(d,e);return e};function Wv(a){this.c=null;W.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,projection:yc("EPSG:3857"),reprojectionErrorThreshold:a.reprojectionErrorThreshold,state:"loading",tileLoadFunction:a.tileLoadFunction,wrapX:void 0!==a.wrapX?a.wrapX:!0});if(a.jsonp)kv(a.url,this.yh.bind(this),this.me.bind(this));else{var b=new XMLHttpRequest;b.addEventListener("load",this.pn.bind(this));b.addEventListener("error",this.nn.bind(this));b.open("GET",a.url);b.send()}} +y(Wv,W);k=Wv.prototype;k.pn=function(a){a=a.target;if(200<=a.status&&300>a.status){var b;try{b=JSON.parse(a.responseText)}catch(c){this.me();return}this.yh(b)}else this.me()};k.nn=function(){this.me()};k.Ak=function(){return this.c}; +k.yh=function(a){var b=yc("EPSG:4326"),c=this.f,d;void 0!==a.bounds&&(d=pc(a.bounds,Bc(b,c)));var e=a.minzoom||0,f=a.maxzoom||22;this.tileGrid=c=yf({extent:wf(c),maxZoom:f,minZoom:e});this.tileUrlFunction=Fl(a.tiles,c);if(void 0!==a.attribution&&!this.l){b=void 0!==d?d:b.H();d={};for(var g;e<=f;++e)g=e.toString(),d[g]=[pf(c,b,e)];this.oa([new je({html:a.attribution,tileRanges:d})])}this.c=a;lf(this,"ready")};k.me=function(){lf(this,"error")};function Xv(a){zf.call(this,{projection:yc("EPSG:3857"),state:"loading"});this.s=void 0!==a.preemptive?a.preemptive:!0;this.j=Hl;this.i=void 0;this.c=a.jsonp||!1;if(a.url)if(this.c)kv(a.url,this.Bf.bind(this),this.ne.bind(this));else{var b=new XMLHttpRequest;b.addEventListener("load",this.tn.bind(this));b.addEventListener("error",this.sn.bind(this));b.open("GET",a.url);b.send()}else a.tileJSON&&this.Bf(a.tileJSON)}y(Xv,zf);k=Xv.prototype; +k.tn=function(a){a=a.target;if(200<=a.status&&300>a.status){var b;try{b=JSON.parse(a.responseText)}catch(c){this.ne();return}this.Bf(b)}else this.ne()};k.sn=function(){this.ne()};k.xk=function(){return this.i};k.Ij=function(a,b,c,d,e){this.tileGrid?(b=this.tileGrid.Zd(a,b),Yv(this.ac(b[0],b[1],b[2],1,this.f),a,c,d,e)):!0===e?Tf(function(){c.call(d,null)}):c.call(d,null)};k.ne=function(){lf(this,"error")}; +k.Bf=function(a){var b=yc("EPSG:4326"),c=this.f,d;void 0!==a.bounds&&(d=pc(a.bounds,Bc(b,c)));var e=a.minzoom||0,f=a.maxzoom||22;this.tileGrid=c=yf({extent:wf(c),maxZoom:f,minZoom:e});this.i=a.template;var g=a.grids;if(g){this.j=Fl(g,c);if(void 0!==a.attribution){b=void 0!==d?d:b.H();for(d={};e<=f;++e)g=e.toString(),d[g]=[pf(c,b,e)];this.oa([new je({html:a.attribution,tileRanges:d})])}lf(this,"ready")}else lf(this,"error")}; +k.ac=function(a,b,c,d,e){var f=this.Eb(a,b,c);if(Ze(this.a,f))return this.a.get(f);a=[a,b,c];b=Cf(this,a,e);d=this.j(b,d,e);d=new Zv(a,void 0!==d?0:4,void 0!==d?d:"",this.tileGrid.Ea(a),this.s,this.c);this.a.set(f,d);return d};k.Yf=function(a,b,c){a=this.Eb(a,b,c);Ze(this.a,a)&&this.a.get(a)};function Zv(a,b,c,d,e,f){df.call(this,a,b);this.s=c;this.g=d;this.U=e;this.c=this.j=this.l=null;this.v=f}y(Zv,df);k=Zv.prototype;k.$a=function(){return null}; +k.getData=function(a){if(!this.l||!this.j)return null;var b=this.l[Math.floor((1-(a[1]-this.g[1])/(this.g[3]-this.g[1]))*this.l.length)];if("string"!==typeof b)return null;b=b.charCodeAt(Math.floor((a[0]-this.g[0])/(this.g[2]-this.g[0])*b.length));93<=b&&b--;35<=b&&b--;b-=32;a=null;b in this.j&&(b=this.j[b],this.c&&b in this.c?a=this.c[b]:a=b);return a}; +function Yv(a,b,c,d,e){0==a.state&&!0===e?(Pa(a,"change",function(){c.call(d,this.getData(b))},a),$v(a)):!0===e?Tf(function(){c.call(d,this.getData(b))},a):c.call(d,a.getData(b))}k.ib=function(){return this.s};k.ae=function(){this.state=3;ef(this)};k.zh=function(a){this.l=a.grid;this.j=a.keys;this.c=a.data;this.state=4;ef(this)}; +function $v(a){if(0==a.state)if(a.state=1,a.v)kv(a.s,a.zh.bind(a),a.ae.bind(a));else{var b=new XMLHttpRequest;b.addEventListener("load",a.rn.bind(a));b.addEventListener("error",a.qn.bind(a));b.open("GET",a.s);b.send()}}k.rn=function(a){a=a.target;if(200<=a.status&&300>a.status){var b;try{b=JSON.parse(a.responseText)}catch(c){this.ae();return}this.zh(b)}else this.ae()};k.qn=function(){this.ae()};k.load=function(){this.U&&$v(this)};function aw(a){a=a||{};var b=a.params||{};W.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,opaque:!("TRANSPARENT"in b?b.TRANSPARENT:1),projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileGrid:a.tileGrid,tileLoadFunction:a.tileLoadFunction,url:a.url,urls:a.urls,wrapX:void 0!==a.wrapX?a.wrapX:!0});this.v=void 0!==a.gutter?a.gutter:0;this.c=b;this.j=!0;this.A=a.serverType;this.T=void 0!==a.hidpi?a.hidpi:!0;this.S=""; +bw(this);this.Y=Lb();cw(this);Bf(this,dw(this))}y(aw,W);k=aw.prototype; +k.vn=function(a,b,c,d){c=yc(c);var e=this.tileGrid;e||(e=this.eb(c));b=e.Zd(a,b);if(!(e.b.length<=b[0])){var f=e.$(b[0]),g=e.Ea(b,this.Y),e=hf(e.Ja(b[0]),this.o),h=this.v;0!==h&&(e=ff(e,h,this.o),g=Ob(g,f*h,g));h={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetFeatureInfo",FORMAT:"image/png",TRANSPARENT:!0,QUERY_LAYERS:this.c.LAYERS};Ea(h,this.c,d);d=Math.floor((g[3]-a[1])/f);h[this.j?"I":"X"]=Math.floor((a[0]-g[0])/f);h[this.j?"J":"Y"]=d;return ew(this,b,e,g,1,c,h)}};k.gf=function(){return this.v}; +k.Eb=function(a,b,c){return this.S+W.prototype.Eb.call(this,a,b,c)};k.wn=function(){return this.c}; +function ew(a,b,c,d,e,f,g){var h=a.urls;if(h){g.WIDTH=c[0];g.HEIGHT=c[1];g[a.j?"CRS":"SRS"]=f.cb;"STYLES"in a.c||(g.STYLES="");if(1!=e)switch(a.A){case "geoserver":c=90*e+.5|0;g.FORMAT_OPTIONS="FORMAT_OPTIONS"in g?g.FORMAT_OPTIONS+(";dpi:"+c):"dpi:"+c;break;case "mapserver":g.MAP_RESOLUTION=90*e;break;case "carmentaserver":case "qgis":g.DPI=90*e}f=f.b;a.j&&"ne"==f.substr(0,2)&&(a=d[0],d[0]=d[1],d[1]=a,a=d[2],d[2]=d[3],d[3]=a);g.BBOX=d.join(",");return xv(1==h.length?h[0]:h[xa((b[1]<<b[0])+b[2],h.length)], +g)}}k.bc=function(a){return this.T&&void 0!==this.A?a:1};function bw(a){var b=0,c=[];if(a.urls){var d,e;d=0;for(e=a.urls.length;d<e;++d)c[b++]=a.urls[d]}a.S=c.join("#")}function dw(a){var b=0,c=[],d;for(d in a.c)c[b++]=d+"-"+a.c[d];return c.join("/")} +k.vc=function(a,b,c){var d=this.tileGrid;d||(d=this.eb(c));if(!(d.b.length<=a[0])){1==b||this.T&&void 0!==this.A||(b=1);var e=d.$(a[0]),f=d.Ea(a,this.Y),d=hf(d.Ja(a[0]),this.o),g=this.v;0!==g&&(d=ff(d,g,this.o),f=Ob(f,e*g,f));1!=b&&(d=gf(d,b,this.o));e={SERVICE:"WMS",VERSION:"1.3.0",REQUEST:"GetMap",FORMAT:"image/png",TRANSPARENT:!0};Ea(e,this.c);return ew(this,a,d,f,b,c,e)}};k.xn=function(a){Ea(this.c,a);bw(this);cw(this);Bf(this,dw(this))};function cw(a){a.j=0<=Ab(a.c.VERSION||"1.3.0")};function fw(a){this.l=a.matrixIds;mf.call(this,{extent:a.extent,origin:a.origin,origins:a.origins,resolutions:a.resolutions,tileSize:a.tileSize,tileSizes:a.tileSizes,sizes:a.sizes})}y(fw,mf);fw.prototype.j=function(){return this.l}; +function gw(a,b){var c=[],d=[],e=[],f=[],g=[],h;h=yc(a.SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"));var l=h.$b(),m="ne"==h.b.substr(0,2);a.TileMatrix.sort(function(a,b){return b.ScaleDenominator-a.ScaleDenominator});a.TileMatrix.forEach(function(a){d.push(a.Identifier);var b=2.8E-4*a.ScaleDenominator/l,h=a.TileWidth,r=a.TileHeight;m?e.push([a.TopLeftCorner[1],a.TopLeftCorner[0]]):e.push(a.TopLeftCorner);c.push(b);f.push(h==r?h:[h,r]);g.push([a.MatrixWidth,-a.MatrixHeight])}); +return new fw({extent:b,origins:e,resolutions:c,matrixIds:d,tileSizes:f,sizes:g})};function Z(a){function b(a){a="KVP"==d?xv(a,f):a.replace(/\{(\w+?)\}/g,function(a,b){return b.toLowerCase()in f?f[b.toLowerCase()]:a});return function(b){if(b){var c={TileMatrix:e.l[b[0]],TileCol:b[1],TileRow:-b[2]-1};Ea(c,g);b=a;return b="KVP"==d?xv(b,c):b.replace(/\{(\w+?)\}/g,function(a,b){return c[b]})}}}this.T=void 0!==a.version?a.version:"1.0.0";this.v=void 0!==a.format?a.format:"image/jpeg";this.c=void 0!==a.dimensions?a.dimensions:{};this.A=a.layer;this.j=a.matrixSet;this.S=a.style;var c= +a.urls;void 0===c&&void 0!==a.url&&(c=Il(a.url));var d=this.Y=void 0!==a.requestEncoding?a.requestEncoding:"KVP",e=a.tileGrid,f={layer:this.A,style:this.S,tilematrixset:this.j};"KVP"==d&&Ea(f,{Service:"WMTS",Request:"GetTile",Version:this.T,Format:this.v});var g=this.c,h=c&&0<c.length?Gl(c.map(b)):Hl;W.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,projection:a.projection,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileClass:a.tileClass, +tileGrid:e,tileLoadFunction:a.tileLoadFunction,tilePixelRatio:a.tilePixelRatio,tileUrlFunction:h,urls:c,wrapX:void 0!==a.wrapX?a.wrapX:!1});Bf(this,hw(this))}y(Z,W);k=Z.prototype;k.Vj=function(){return this.c};k.yn=function(){return this.v};k.zn=function(){return this.A};k.hk=function(){return this.j};k.vk=function(){return this.Y};k.An=function(){return this.S};k.Ck=function(){return this.T};function hw(a){var b=0,c=[],d;for(d in a.c)c[b++]=d+"-"+a.c[d];return c.join("/")} +k.vp=function(a){Ea(this.c,a);Bf(this,hw(this))};function iw(a){a=a||{};var b=a.size,c=b[0],d=b[1],e=[],f=256;switch(void 0!==a.tierSizeCalculation?a.tierSizeCalculation:"default"){case "default":for(;c>f||d>f;)e.push([Math.ceil(c/f),Math.ceil(d/f)]),f+=f;break;case "truncated":for(;c>f||d>f;)e.push([Math.ceil(c/f),Math.ceil(d/f)]),c>>=1,d>>=1}e.push([1,1]);e.reverse();for(var f=[1],g=[0],d=1,c=e.length;d<c;d++)f.push(1<<d),g.push(e[d-1][0]*e[d-1][1]+g[d-1]);f.reverse();var b=[0,-b[1],b[0],0],b=new mf({extent:b,origin:fc(b),resolutions:f}),h=a.url; +W.call(this,{attributions:a.attributions,cacheSize:a.cacheSize,crossOrigin:a.crossOrigin,logo:a.logo,reprojectionErrorThreshold:a.reprojectionErrorThreshold,tileClass:jw,tileGrid:b,tileUrlFunction:function(a){if(a){var b=a[0],c=a[1];a=-a[2]-1;return h+"TileGroup"+((c+a*e[b][0]+g[b])/256|0)+"/"+b+"-"+c+"-"+a+".jpg"}}})}y(iw,W);function jw(a,b,c,d,e){fu.call(this,a,b,c,d,e);this.l={}}y(jw,fu); +jw.prototype.$a=function(a){var b=void 0!==a?w(a).toString():"";if(b in this.l)return this.l[b];a=fu.prototype.$a.call(this,a);if(2==this.state){if(256==a.width&&256==a.height)return this.l[b]=a;var c=Oe(256,256);c.drawImage(a,0,0);return this.l[b]=c.canvas}return a};function kw(a){a=a||{};this.a=void 0!==a.initialSize?a.initialSize:256;this.g=void 0!==a.maxSize?a.maxSize:void 0!==la?la:2048;this.b=void 0!==a.space?a.space:1;this.c=[new lw(this.a,this.b)];this.f=this.a;this.i=[new lw(this.f,this.b)]}kw.prototype.add=function(a,b,c,d,e,f){if(b+this.b>this.g||c+this.b>this.g)return null;d=mw(this,!1,a,b,c,d,f);if(!d)return null;a=mw(this,!0,a,b,c,void 0!==e?e:na,f);return{offsetX:d.offsetX,offsetY:d.offsetY,image:d.image,Sg:a.image}}; +function mw(a,b,c,d,e,f,g){var h=b?a.i:a.c,l,m,n;m=0;for(n=h.length;m<n;++m){l=h[m];if(l=l.add(c,d,e,f,g))return l;l||m!==n-1||(b?(l=Math.min(2*a.f,a.g),a.f=l):(l=Math.min(2*a.a,a.g),a.a=l),l=new lw(l,a.b),h.push(l),++n)}}function lw(a,b){this.b=b;this.a=[{x:0,y:0,width:a,height:a}];this.f={};this.g=Oe(a,a);this.c=this.g.canvas}lw.prototype.get=function(a){return this.f[a]||null}; +lw.prototype.add=function(a,b,c,d,e){var f,g,h;g=0;for(h=this.a.length;g<h;++g)if(f=this.a[g],f.width>=b+this.b&&f.height>=c+this.b)return h={offsetX:f.x+this.b,offsetY:f.y+this.b,image:this.c},this.f[a]=h,d.call(e,this.g,f.x+this.b,f.y+this.b),a=g,b+=this.b,d=c+this.b,f.width-b>f.height-d?(c={x:f.x+b,y:f.y,width:f.width-b,height:f.height},b={x:f.x,y:f.y+d,width:b,height:f.height-d},nw(this,a,c,b)):(c={x:f.x+b,y:f.y,width:f.width-b,height:d},b={x:f.x,y:f.y+d,width:f.width,height:f.height-d},nw(this, +a,c,b)),h;return null};function nw(a,b,c,d){b=[b,1];0<c.width&&0<c.height&&b.push(c);0<d.width&&0<d.height&&b.push(d);a.a.splice.apply(a.a,b)};function ow(a){this.A=this.s=this.f=null;this.o=void 0!==a.fill?a.fill:null;this.ya=[0,0];this.b=a.points;this.g=void 0!==a.radius?a.radius:a.radius1;this.c=void 0!==a.radius2?a.radius2:this.g;this.l=void 0!==a.angle?a.angle:0;this.a=void 0!==a.stroke?a.stroke:null;this.R=this.Ba=this.D=null;var b=a.atlasManager,c="",d="",e=0,f=null,g,h=0;this.a&&(g=ve(this.a.b),h=this.a.a,void 0===h&&(h=1),f=this.a.g,hg||(f=null),d=this.a.c,void 0===d&&(d="round"),c=this.a.f,void 0===c&&(c="round"),e=this.a.i,void 0=== +e&&(e=10));var l=2*(this.g+h)+1,c={strokeStyle:g,Bd:h,size:l,lineCap:c,lineDash:f,lineJoin:d,miterLimit:e};if(void 0===b){var m=Oe(l,l);this.s=m.canvas;b=l=this.s.width;this.Ih(c,m,0,0);this.o?this.A=this.s:(m=Oe(c.size,c.size),this.A=m.canvas,this.Hh(c,m,0,0))}else l=Math.round(l),(d=!this.o)&&(m=this.Hh.bind(this,c)),e=this.a?pj(this.a):"-",f=this.o?jj(this.o):"-",this.f&&e==this.f[1]&&f==this.f[2]&&this.g==this.f[3]&&this.c==this.f[4]&&this.l==this.f[5]&&this.b==this.f[6]||(this.f=["r"+e+f+(void 0!== +this.g?this.g.toString():"-")+(void 0!==this.c?this.c.toString():"-")+(void 0!==this.l?this.l.toString():"-")+(void 0!==this.b?this.b.toString():"-"),e,f,this.g,this.c,this.l,this.b]),m=b.add(this.f[0],l,l,this.Ih.bind(this,c),m),this.s=m.image,this.ya=[m.offsetX,m.offsetY],b=m.image.width,this.A=d?m.Sg:this.s;this.D=[l/2,l/2];this.Ba=[l,l];this.R=[b,b];Ch.call(this,{opacity:1,rotateWithView:void 0!==a.rotateWithView?a.rotateWithView:!1,rotation:void 0!==a.rotation?a.rotation:0,scale:1,snapToPixel:void 0!== +a.snapToPixel?a.snapToPixel:!0})}y(ow,Ch);k=ow.prototype;k.Yb=function(){return this.D};k.Fn=function(){return this.l};k.Gn=function(){return this.o};k.pe=function(){return this.A};k.jc=function(){return this.s};k.ld=function(){return this.R};k.td=function(){return 2};k.Ia=function(){return this.ya};k.Hn=function(){return this.b};k.In=function(){return this.g};k.uk=function(){return this.c};k.Fb=function(){return this.Ba};k.Jn=function(){return this.a};k.pf=na;k.load=na;k.Xf=na; +k.Ih=function(a,b,c,d){var e;b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();this.c!==this.g&&(this.b*=2);for(c=0;c<=this.b;c++)d=2*c*Math.PI/this.b-Math.PI/2+this.l,e=0===c%2?this.g:this.c,b.lineTo(a.size/2+e*Math.cos(d),a.size/2+e*Math.sin(d));this.o&&(b.fillStyle=xe(this.o.b),b.fill());this.a&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.Bd,a.lineDash&&b.setLineDash(a.lineDash),b.lineCap=a.lineCap,b.lineJoin=a.lineJoin,b.miterLimit=a.miterLimit,b.stroke());b.closePath()}; +k.Hh=function(a,b,c,d){b.setTransform(1,0,0,1,0,0);b.translate(c,d);b.beginPath();this.c!==this.g&&(this.b*=2);var e;for(c=0;c<=this.b;c++)e=2*c*Math.PI/this.b-Math.PI/2+this.l,d=0===c%2?this.g:this.c,b.lineTo(a.size/2+d*Math.cos(e),a.size/2+d*Math.sin(e));b.fillStyle=ej;b.fill();this.a&&(b.strokeStyle=a.strokeStyle,b.lineWidth=a.Bd,a.lineDash&&b.setLineDash(a.lineDash),b.stroke());b.closePath()};t("ol.animation.bounce",function(a){var b=a.resolution,c=a.start?a.start:Date.now(),d=void 0!==a.duration?a.duration:1E3,e=a.easing?a.easing:be;return function(a,g){if(g.time<c)return g.animate=!0,g.viewHints[0]+=1,!0;if(g.time<c+d){var h=e((g.time-c)/d),l=b-g.viewState.resolution;g.animate=!0;g.viewState.resolution+=h*l;g.viewHints[0]+=1;return!0}return!1}},OPENLAYERS);t("ol.animation.pan",ce,OPENLAYERS);t("ol.animation.rotate",de,OPENLAYERS);t("ol.animation.zoom",ee,OPENLAYERS); +t("ol.Attribution",je,OPENLAYERS);je.prototype.getHTML=je.prototype.g;ke.prototype.element=ke.prototype.element;t("ol.Collection",le,OPENLAYERS);le.prototype.clear=le.prototype.clear;le.prototype.extend=le.prototype.qf;le.prototype.forEach=le.prototype.forEach;le.prototype.getArray=le.prototype.Gl;le.prototype.item=le.prototype.item;le.prototype.getLength=le.prototype.dc;le.prototype.insertAt=le.prototype.ee;le.prototype.pop=le.prototype.pop;le.prototype.push=le.prototype.push; +le.prototype.remove=le.prototype.remove;le.prototype.removeAt=le.prototype.Rf;le.prototype.setAt=le.prototype.Zo;t("ol.colorlike.asColorLike",xe,OPENLAYERS);t("ol.coordinate.add",Bb,OPENLAYERS);t("ol.coordinate.createStringXY",function(a){return function(b){return Jb(b,a)}},OPENLAYERS);t("ol.coordinate.format",Eb,OPENLAYERS);t("ol.coordinate.rotate",Gb,OPENLAYERS);t("ol.coordinate.toStringHDMS",function(a,b){return a?Db(a[1],"NS",b)+" "+Db(a[0],"EW",b):""},OPENLAYERS); +t("ol.coordinate.toStringXY",Jb,OPENLAYERS);t("ol.DeviceOrientation",qn,OPENLAYERS);qn.prototype.getAlpha=qn.prototype.Oj;qn.prototype.getBeta=qn.prototype.Rj;qn.prototype.getGamma=qn.prototype.Yj;qn.prototype.getHeading=qn.prototype.Hl;qn.prototype.getTracking=qn.prototype.$g;qn.prototype.setTracking=qn.prototype.rf;t("ol.easing.easeIn",Yd,OPENLAYERS);t("ol.easing.easeOut",Zd,OPENLAYERS);t("ol.easing.inAndOut",$d,OPENLAYERS);t("ol.easing.linear",ae,OPENLAYERS);t("ol.easing.upAndDown",be,OPENLAYERS); +t("ol.extent.boundingExtent",Kb,OPENLAYERS);t("ol.extent.buffer",Ob,OPENLAYERS);t("ol.extent.containsCoordinate",Sb,OPENLAYERS);t("ol.extent.containsExtent",Ub,OPENLAYERS);t("ol.extent.containsXY",Tb,OPENLAYERS);t("ol.extent.createEmpty",Lb,OPENLAYERS);t("ol.extent.equals",$b,OPENLAYERS);t("ol.extent.extend",ac,OPENLAYERS);t("ol.extent.getBottomLeft",cc,OPENLAYERS);t("ol.extent.getBottomRight",dc,OPENLAYERS);t("ol.extent.getCenter",kc,OPENLAYERS);t("ol.extent.getHeight",jc,OPENLAYERS); +t("ol.extent.getIntersection",mc,OPENLAYERS);t("ol.extent.getSize",function(a){return[a[2]-a[0],a[3]-a[1]]},OPENLAYERS);t("ol.extent.getTopLeft",fc,OPENLAYERS);t("ol.extent.getTopRight",ec,OPENLAYERS);t("ol.extent.getWidth",ic,OPENLAYERS);t("ol.extent.intersects",nc,OPENLAYERS);t("ol.extent.isEmpty",hc,OPENLAYERS);t("ol.extent.applyTransform",pc,OPENLAYERS);t("ol.Feature",Ik,OPENLAYERS);Ik.prototype.clone=Ik.prototype.clone;Ik.prototype.getGeometry=Ik.prototype.W;Ik.prototype.getId=Ik.prototype.Xa; +Ik.prototype.getGeometryName=Ik.prototype.$j;Ik.prototype.getStyle=Ik.prototype.Jl;Ik.prototype.getStyleFunction=Ik.prototype.ec;Ik.prototype.setGeometry=Ik.prototype.Ua;Ik.prototype.setStyle=Ik.prototype.sf;Ik.prototype.setId=Ik.prototype.mc;Ik.prototype.setGeometryName=Ik.prototype.Ec;t("ol.featureloader.tile",dl,OPENLAYERS);t("ol.featureloader.xhr",el,OPENLAYERS);t("ol.Geolocation",Ut,OPENLAYERS);Ut.prototype.getAccuracy=Ut.prototype.Mj;Ut.prototype.getAccuracyGeometry=Ut.prototype.Nj; +Ut.prototype.getAltitude=Ut.prototype.Pj;Ut.prototype.getAltitudeAccuracy=Ut.prototype.Qj;Ut.prototype.getHeading=Ut.prototype.Ll;Ut.prototype.getPosition=Ut.prototype.Ml;Ut.prototype.getProjection=Ut.prototype.ah;Ut.prototype.getSpeed=Ut.prototype.wk;Ut.prototype.getTracking=Ut.prototype.bh;Ut.prototype.getTrackingOptions=Ut.prototype.Mg;Ut.prototype.setProjection=Ut.prototype.dh;Ut.prototype.setTracking=Ut.prototype.ge;Ut.prototype.setTrackingOptions=Ut.prototype.si;t("ol.Graticule",$t,OPENLAYERS); +$t.prototype.getMap=$t.prototype.Pl;$t.prototype.getMeridians=$t.prototype.ik;$t.prototype.getParallels=$t.prototype.qk;$t.prototype.setMap=$t.prototype.setMap;t("ol.has.DEVICE_PIXEL_RATIO",gg,OPENLAYERS);t("ol.has.CANVAS",ig,OPENLAYERS);t("ol.has.DEVICE_ORIENTATION",jg,OPENLAYERS);t("ol.has.GEOLOCATION",kg,OPENLAYERS);t("ol.has.TOUCH",lg,OPENLAYERS);t("ol.has.WEBGL",bg,OPENLAYERS);eu.prototype.getImage=eu.prototype.a;eu.prototype.load=eu.prototype.load;fu.prototype.getImage=fu.prototype.$a; +fu.prototype.load=fu.prototype.load;t("ol.Kinetic",Th,OPENLAYERS);t("ol.loadingstrategy.all",fl,OPENLAYERS);t("ol.loadingstrategy.bbox",function(a){return[a]},OPENLAYERS);t("ol.loadingstrategy.tile",function(a){return function(b,c){var d=a.Lb(c),e=pf(a,b,d),f=[],d=[d,0,0];for(d[1]=e.ca;d[1]<=e.ea;++d[1])for(d[2]=e.fa;d[2]<=e.ga;++d[2])f.push(a.Ea(d));return f}},OPENLAYERS);t("ol.Map",Q,OPENLAYERS);Q.prototype.addControl=Q.prototype.uj;Q.prototype.addInteraction=Q.prototype.vj; +Q.prototype.addLayer=Q.prototype.kg;Q.prototype.addOverlay=Q.prototype.lg;Q.prototype.beforeRender=Q.prototype.Wa;Q.prototype.forEachFeatureAtPixel=Q.prototype.kd;Q.prototype.forEachLayerAtPixel=Q.prototype.Tl;Q.prototype.hasFeatureAtPixel=Q.prototype.kl;Q.prototype.getEventCoordinate=Q.prototype.Wj;Q.prototype.getEventPixel=Q.prototype.Td;Q.prototype.getTarget=Q.prototype.tf;Q.prototype.getTargetElement=Q.prototype.yc;Q.prototype.getCoordinateFromPixel=Q.prototype.Ma;Q.prototype.getControls=Q.prototype.Uj; +Q.prototype.getOverlays=Q.prototype.nk;Q.prototype.getOverlayById=Q.prototype.mk;Q.prototype.getInteractions=Q.prototype.ak;Q.prototype.getLayerGroup=Q.prototype.xc;Q.prototype.getLayers=Q.prototype.eh;Q.prototype.getPixelFromCoordinate=Q.prototype.Ga;Q.prototype.getSize=Q.prototype.Za;Q.prototype.getView=Q.prototype.aa;Q.prototype.getViewport=Q.prototype.Dk;Q.prototype.renderSync=Q.prototype.Vo;Q.prototype.render=Q.prototype.render;Q.prototype.removeControl=Q.prototype.Oo; +Q.prototype.removeInteraction=Q.prototype.Po;Q.prototype.removeLayer=Q.prototype.Ro;Q.prototype.removeOverlay=Q.prototype.So;Q.prototype.setLayerGroup=Q.prototype.ji;Q.prototype.setSize=Q.prototype.Wf;Q.prototype.setTarget=Q.prototype.fh;Q.prototype.setView=Q.prototype.kp;Q.prototype.updateSize=Q.prototype.Xc;Vg.prototype.originalEvent=Vg.prototype.originalEvent;Vg.prototype.pixel=Vg.prototype.pixel;Vg.prototype.coordinate=Vg.prototype.coordinate;Vg.prototype.dragging=Vg.prototype.dragging; +We.prototype.map=We.prototype.map;We.prototype.frameState=We.prototype.frameState;db.prototype.key=db.prototype.key;db.prototype.oldValue=db.prototype.oldValue;t("ol.Object",eb,OPENLAYERS);eb.prototype.get=eb.prototype.get;eb.prototype.getKeys=eb.prototype.N;eb.prototype.getProperties=eb.prototype.O;eb.prototype.set=eb.prototype.set;eb.prototype.setProperties=eb.prototype.G;eb.prototype.unset=eb.prototype.P;t("ol.Observable",bb,OPENLAYERS);t("ol.Observable.unByKey",cb,OPENLAYERS); +bb.prototype.changed=bb.prototype.u;bb.prototype.dispatchEvent=bb.prototype.b;bb.prototype.getRevision=bb.prototype.K;bb.prototype.on=bb.prototype.I;bb.prototype.once=bb.prototype.L;bb.prototype.un=bb.prototype.J;bb.prototype.unByKey=bb.prototype.M;t("ol.inherits",y,OPENLAYERS);t("ol.Overlay",Xm,OPENLAYERS);Xm.prototype.getElement=Xm.prototype.Sd;Xm.prototype.getId=Xm.prototype.Xa;Xm.prototype.getMap=Xm.prototype.he;Xm.prototype.getOffset=Xm.prototype.Kg;Xm.prototype.getPosition=Xm.prototype.gh; +Xm.prototype.getPositioning=Xm.prototype.Lg;Xm.prototype.setElement=Xm.prototype.fi;Xm.prototype.setMap=Xm.prototype.setMap;Xm.prototype.setOffset=Xm.prototype.li;Xm.prototype.setPosition=Xm.prototype.uf;Xm.prototype.setPositioning=Xm.prototype.oi;t("ol.render.toContext",function(a,b){var c=a.canvas,d=b?b:{},e=d.pixelRatio||gg;if(d=d.size)c.width=d[0]*e,c.height=d[1]*e,c.style.width=d[0]+"px",c.style.height=d[1]+"px";c=[0,0,c.width,c.height];d=qh(Xc(),0,0,e,e,0,0,0);return new yj(a,e,c,d,0)},OPENLAYERS); +t("ol.size.toSize",hf,OPENLAYERS);df.prototype.getTileCoord=df.prototype.i;df.prototype.load=df.prototype.load;Kk.prototype.getFormat=Kk.prototype.Ul;Kk.prototype.setFeatures=Kk.prototype.gi;Kk.prototype.setProjection=Kk.prototype.vf;Kk.prototype.setLoader=Kk.prototype.ki;t("ol.View",Rd,OPENLAYERS);Rd.prototype.constrainCenter=Rd.prototype.Pd;Rd.prototype.constrainResolution=Rd.prototype.constrainResolution;Rd.prototype.constrainRotation=Rd.prototype.constrainRotation;Rd.prototype.getCenter=Rd.prototype.ab; +Rd.prototype.calculateExtent=Rd.prototype.Kc;Rd.prototype.getMaxResolution=Rd.prototype.Vl;Rd.prototype.getMinResolution=Rd.prototype.Wl;Rd.prototype.getProjection=Rd.prototype.Xl;Rd.prototype.getResolution=Rd.prototype.$;Rd.prototype.getResolutions=Rd.prototype.Yl;Rd.prototype.getRotation=Rd.prototype.La;Rd.prototype.getZoom=Rd.prototype.Fk;Rd.prototype.fit=Rd.prototype.cf;Rd.prototype.centerOn=Rd.prototype.Ej;Rd.prototype.rotate=Rd.prototype.rotate;Rd.prototype.setCenter=Rd.prototype.mb; +Rd.prototype.setResolution=Rd.prototype.Ub;Rd.prototype.setRotation=Rd.prototype.ie;Rd.prototype.setZoom=Rd.prototype.np;t("ol.xml.getAllTextContent",Nk,OPENLAYERS);t("ol.xml.parse",Rk,OPENLAYERS);gm.prototype.getGL=gm.prototype.$n;gm.prototype.useProgram=gm.prototype.we;t("ol.tilegrid.TileGrid",mf,OPENLAYERS);mf.prototype.forEachTileCoord=mf.prototype.yg;mf.prototype.getMaxZoom=mf.prototype.Ig;mf.prototype.getMinZoom=mf.prototype.Jg;mf.prototype.getOrigin=mf.prototype.Ia; +mf.prototype.getResolution=mf.prototype.$;mf.prototype.getResolutions=mf.prototype.Kh;mf.prototype.getTileCoordExtent=mf.prototype.Ea;mf.prototype.getTileCoordForCoordAndResolution=mf.prototype.Zd;mf.prototype.getTileCoordForCoordAndZ=mf.prototype.qd;mf.prototype.getTileSize=mf.prototype.Ja;mf.prototype.getZForResolution=mf.prototype.Lb;t("ol.tilegrid.createXYZ",yf,OPENLAYERS);t("ol.tilegrid.WMTS",fw,OPENLAYERS);fw.prototype.getMatrixIds=fw.prototype.j; +t("ol.tilegrid.WMTS.createFromCapabilitiesMatrixSet",gw,OPENLAYERS);t("ol.style.AtlasManager",kw,OPENLAYERS);t("ol.style.Circle",qj,OPENLAYERS);qj.prototype.getFill=qj.prototype.Bn;qj.prototype.getImage=qj.prototype.jc;qj.prototype.getRadius=qj.prototype.Cn;qj.prototype.getStroke=qj.prototype.Dn;t("ol.style.Fill",ij,OPENLAYERS);ij.prototype.getColor=ij.prototype.g;ij.prototype.setColor=ij.prototype.f;t("ol.style.Icon",Dh,OPENLAYERS);Dh.prototype.getAnchor=Dh.prototype.Yb;Dh.prototype.getImage=Dh.prototype.jc; +Dh.prototype.getOrigin=Dh.prototype.Ia;Dh.prototype.getSrc=Dh.prototype.En;Dh.prototype.getSize=Dh.prototype.Fb;Dh.prototype.load=Dh.prototype.load;t("ol.style.Image",Ch,OPENLAYERS);Ch.prototype.getOpacity=Ch.prototype.qe;Ch.prototype.getRotateWithView=Ch.prototype.Xd;Ch.prototype.getRotation=Ch.prototype.re;Ch.prototype.getScale=Ch.prototype.se;Ch.prototype.getSnapToPixel=Ch.prototype.Yd;Ch.prototype.setOpacity=Ch.prototype.te;Ch.prototype.setRotation=Ch.prototype.ue;Ch.prototype.setScale=Ch.prototype.ve; +t("ol.style.RegularShape",ow,OPENLAYERS);ow.prototype.getAnchor=ow.prototype.Yb;ow.prototype.getAngle=ow.prototype.Fn;ow.prototype.getFill=ow.prototype.Gn;ow.prototype.getImage=ow.prototype.jc;ow.prototype.getOrigin=ow.prototype.Ia;ow.prototype.getPoints=ow.prototype.Hn;ow.prototype.getRadius=ow.prototype.In;ow.prototype.getRadius2=ow.prototype.uk;ow.prototype.getSize=ow.prototype.Fb;ow.prototype.getStroke=ow.prototype.Jn;t("ol.style.Stroke",oj,OPENLAYERS);oj.prototype.getColor=oj.prototype.Kn; +oj.prototype.getLineCap=oj.prototype.dk;oj.prototype.getLineDash=oj.prototype.Ln;oj.prototype.getLineJoin=oj.prototype.ek;oj.prototype.getMiterLimit=oj.prototype.jk;oj.prototype.getWidth=oj.prototype.Mn;oj.prototype.setColor=oj.prototype.Nn;oj.prototype.setLineCap=oj.prototype.fp;oj.prototype.setLineDash=oj.prototype.On;oj.prototype.setLineJoin=oj.prototype.gp;oj.prototype.setMiterLimit=oj.prototype.hp;oj.prototype.setWidth=oj.prototype.lp;t("ol.style.Style",rj,OPENLAYERS); +rj.prototype.getGeometry=rj.prototype.W;rj.prototype.getGeometryFunction=rj.prototype.Zj;rj.prototype.getFill=rj.prototype.Pn;rj.prototype.getImage=rj.prototype.Qn;rj.prototype.getStroke=rj.prototype.Rn;rj.prototype.getText=rj.prototype.Ha;rj.prototype.getZIndex=rj.prototype.Sn;rj.prototype.setGeometry=rj.prototype.Jh;rj.prototype.setZIndex=rj.prototype.Tn;t("ol.style.Text",Gp,OPENLAYERS);Gp.prototype.getFont=Gp.prototype.Xj;Gp.prototype.getOffsetX=Gp.prototype.kk;Gp.prototype.getOffsetY=Gp.prototype.lk; +Gp.prototype.getFill=Gp.prototype.Un;Gp.prototype.getRotation=Gp.prototype.Vn;Gp.prototype.getScale=Gp.prototype.Wn;Gp.prototype.getStroke=Gp.prototype.Xn;Gp.prototype.getText=Gp.prototype.Ha;Gp.prototype.getTextAlign=Gp.prototype.yk;Gp.prototype.getTextBaseline=Gp.prototype.zk;Gp.prototype.setFont=Gp.prototype.bp;Gp.prototype.setOffsetX=Gp.prototype.mi;Gp.prototype.setOffsetY=Gp.prototype.ni;Gp.prototype.setFill=Gp.prototype.ap;Gp.prototype.setRotation=Gp.prototype.Yn;Gp.prototype.setScale=Gp.prototype.Zn; +Gp.prototype.setStroke=Gp.prototype.ip;Gp.prototype.setText=Gp.prototype.pi;Gp.prototype.setTextAlign=Gp.prototype.ri;Gp.prototype.setTextBaseline=Gp.prototype.jp;t("ol.Sphere",sc,OPENLAYERS);sc.prototype.geodesicArea=sc.prototype.a;sc.prototype.haversineDistance=sc.prototype.b;t("ol.source.BingMaps",pv,OPENLAYERS);t("ol.source.BingMaps.TOS_ATTRIBUTION",qv,OPENLAYERS);t("ol.source.CartoDB",sv,OPENLAYERS);sv.prototype.getConfig=sv.prototype.Tj;sv.prototype.updateConfig=sv.prototype.up; +sv.prototype.setConfig=sv.prototype.$o;t("ol.source.Cluster",Y,OPENLAYERS);Y.prototype.getSource=Y.prototype.Aa;t("ol.source.ImageArcGISRest",yv,OPENLAYERS);yv.prototype.getParams=yv.prototype.Sm;yv.prototype.getImageLoadFunction=yv.prototype.Rm;yv.prototype.getUrl=yv.prototype.Tm;yv.prototype.setImageLoadFunction=yv.prototype.Um;yv.prototype.setUrl=yv.prototype.Vm;yv.prototype.updateParams=yv.prototype.Wm;t("ol.source.ImageCanvas",Hk,OPENLAYERS);t("ol.source.ImageMapGuide",zv,OPENLAYERS); +zv.prototype.getParams=zv.prototype.Ym;zv.prototype.getImageLoadFunction=zv.prototype.Xm;zv.prototype.updateParams=zv.prototype.$m;zv.prototype.setImageLoadFunction=zv.prototype.Zm;t("ol.source.Image",Ak,OPENLAYERS);Ck.prototype.image=Ck.prototype.image;t("ol.source.ImageStatic",Av,OPENLAYERS);t("ol.source.ImageVector",yl,OPENLAYERS);yl.prototype.getSource=yl.prototype.an;yl.prototype.getStyle=yl.prototype.bn;yl.prototype.getStyleFunction=yl.prototype.cn;yl.prototype.setStyle=yl.prototype.xh; +t("ol.source.ImageWMS",Bv,OPENLAYERS);Bv.prototype.getGetFeatureInfoUrl=Bv.prototype.fn;Bv.prototype.getParams=Bv.prototype.hn;Bv.prototype.getImageLoadFunction=Bv.prototype.gn;Bv.prototype.getUrl=Bv.prototype.jn;Bv.prototype.setImageLoadFunction=Bv.prototype.kn;Bv.prototype.setUrl=Bv.prototype.ln;Bv.prototype.updateParams=Bv.prototype.mn;t("ol.source.OSM",Fv,OPENLAYERS);t("ol.source.OSM.ATTRIBUTION",Gv,OPENLAYERS);t("ol.source.Raster",Hv,OPENLAYERS);Hv.prototype.setOperation=Hv.prototype.v; +Mv.prototype.extent=Mv.prototype.extent;Mv.prototype.resolution=Mv.prototype.resolution;Mv.prototype.data=Mv.prototype.data;t("ol.source.Source",jf,OPENLAYERS);jf.prototype.getAttributions=jf.prototype.wa;jf.prototype.getLogo=jf.prototype.ua;jf.prototype.getProjection=jf.prototype.xa;jf.prototype.getState=jf.prototype.V;jf.prototype.refresh=jf.prototype.sa;jf.prototype.setAttributions=jf.prototype.oa;t("ol.source.Stamen",Rv,OPENLAYERS);t("ol.source.TileArcGISRest",Tv,OPENLAYERS); +Tv.prototype.getParams=Tv.prototype.v;Tv.prototype.updateParams=Tv.prototype.A;t("ol.source.TileDebug",Vv,OPENLAYERS);t("ol.source.TileImage",W,OPENLAYERS);W.prototype.setRenderReprojectionEdges=W.prototype.zb;W.prototype.setTileGridForProjection=W.prototype.Ab;t("ol.source.TileJSON",Wv,OPENLAYERS);Wv.prototype.getTileJSON=Wv.prototype.Ak;t("ol.source.Tile",zf,OPENLAYERS);zf.prototype.getTileGrid=zf.prototype.Na;Df.prototype.tile=Df.prototype.tile;t("ol.source.TileUTFGrid",Xv,OPENLAYERS); +Xv.prototype.getTemplate=Xv.prototype.xk;Xv.prototype.forDataAtCoordinateAndResolution=Xv.prototype.Ij;t("ol.source.TileWMS",aw,OPENLAYERS);aw.prototype.getGetFeatureInfoUrl=aw.prototype.vn;aw.prototype.getParams=aw.prototype.wn;aw.prototype.updateParams=aw.prototype.xn;Jl.prototype.getTileLoadFunction=Jl.prototype.fb;Jl.prototype.getTileUrlFunction=Jl.prototype.gb;Jl.prototype.getUrls=Jl.prototype.hb;Jl.prototype.setTileLoadFunction=Jl.prototype.kb;Jl.prototype.setTileUrlFunction=Jl.prototype.Qa; +Jl.prototype.setUrl=Jl.prototype.Va;Jl.prototype.setUrls=Jl.prototype.bb;t("ol.source.Vector",P,OPENLAYERS);P.prototype.addFeature=P.prototype.rb;P.prototype.addFeatures=P.prototype.Jc;P.prototype.clear=P.prototype.clear;P.prototype.forEachFeature=P.prototype.wg;P.prototype.forEachFeatureInExtent=P.prototype.ub;P.prototype.forEachFeatureIntersectingExtent=P.prototype.xg;P.prototype.getFeaturesCollection=P.prototype.Fg;P.prototype.getFeatures=P.prototype.oe;P.prototype.getFeaturesAtCoordinate=P.prototype.Eg; +P.prototype.getFeaturesInExtent=P.prototype.ef;P.prototype.getClosestFeatureToCoordinate=P.prototype.Ag;P.prototype.getExtent=P.prototype.H;P.prototype.getFeatureById=P.prototype.Dg;P.prototype.getFormat=P.prototype.Ch;P.prototype.getUrl=P.prototype.Dh;P.prototype.removeFeature=P.prototype.nb;vl.prototype.feature=vl.prototype.feature;t("ol.source.VectorTile",Kl,OPENLAYERS);t("ol.source.WMTS",Z,OPENLAYERS);Z.prototype.getDimensions=Z.prototype.Vj;Z.prototype.getFormat=Z.prototype.yn; +Z.prototype.getLayer=Z.prototype.zn;Z.prototype.getMatrixSet=Z.prototype.hk;Z.prototype.getRequestEncoding=Z.prototype.vk;Z.prototype.getStyle=Z.prototype.An;Z.prototype.getVersion=Z.prototype.Ck;Z.prototype.updateDimensions=Z.prototype.vp; +t("ol.source.WMTS.optionsFromCapabilities",function(a,b){var c=ob(a.Contents.Layer,function(a){return a.Identifier==b.layer}),d=a.Contents.TileMatrixSet,e,f;e=1<c.TileMatrixSetLink.length?"projection"in b?sb(c.TileMatrixSetLink,function(a){return ob(d,function(b){return b.Identifier==a.TileMatrixSet}).SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3")==b.projection}):sb(c.TileMatrixSetLink,function(a){return a.TileMatrixSet==b.matrixSet}):0;0>e&&(e=0);f=c.TileMatrixSetLink[e].TileMatrixSet; +var g=c.Format[0];"format"in b&&(g=b.format);e=sb(c.Style,function(a){return"style"in b?a.Title==b.style:a.isDefault});0>e&&(e=0);e=c.Style[e].Identifier;var h={};"Dimension"in c&&c.Dimension.forEach(function(a){var b=a.Identifier,c=a.Default;void 0===c&&(c=a.Value[0]);h[b]=c});var l=ob(a.Contents.TileMatrixSet,function(a){return a.Identifier==f}),m;m="projection"in b?yc(b.projection):yc(l.SupportedCRS.replace(/urn:ogc:def:crs:(\w+):(.*:)?(\w+)$/,"$1:$3"));var n=c.WGS84BoundingBox,p,q;void 0!==n&& +(q=yc("EPSG:4326").H(),q=n[0]==q[0]&&n[2]==q[2],p=Sc(n,"EPSG:4326",m),(n=m.H())&&(Ub(n,p)||(p=void 0)));var l=gw(l,p),r=[];p=b.requestEncoding;p=void 0!==p?p:"";if("OperationsMetadata"in a&&"GetTile"in a.OperationsMetadata)for(var n=a.OperationsMetadata.GetTile.DCP.HTTP.Get,u=0,x=n.length;u<x;++u){var v=ob(n[u].Constraint,function(a){return"GetEncoding"==a.name}).AllowedValues.Value;""===p&&(p=v[0]);if("KVP"===p)jb(v,"KVP")&&r.push(n[u].href);else break}0===r.length&&(p="REST",c.ResourceURL.forEach(function(a){"tile"=== +a.resourceType&&(g=a.format,r.push(a.template))}));return{urls:r,layer:b.layer,matrixSet:f,format:g,projection:m,requestEncoding:p,tileGrid:l,style:e,dimensions:h,wrapX:q}},OPENLAYERS);t("ol.source.XYZ",rv,OPENLAYERS);t("ol.source.Zoomify",iw,OPENLAYERS);lh.prototype.vectorContext=lh.prototype.vectorContext;lh.prototype.frameState=lh.prototype.frameState;lh.prototype.context=lh.prototype.context;lh.prototype.glContext=lh.prototype.glContext;hk.prototype.get=hk.prototype.get; +hk.prototype.getExtent=hk.prototype.H;hk.prototype.getGeometry=hk.prototype.W;hk.prototype.getProperties=hk.prototype.Nm;hk.prototype.getType=hk.prototype.X;t("ol.render.VectorContext",kh,OPENLAYERS);Cm.prototype.setStyle=Cm.prototype.sd;Cm.prototype.drawGeometry=Cm.prototype.sc;Cm.prototype.drawFeature=Cm.prototype.Ye;yj.prototype.drawCircle=yj.prototype.Rd;yj.prototype.setStyle=yj.prototype.sd;yj.prototype.drawGeometry=yj.prototype.sc;yj.prototype.drawFeature=yj.prototype.Ye; +t("ol.proj.common.add",bj,OPENLAYERS);t("ol.proj.METERS_PER_UNIT",uc,OPENLAYERS);t("ol.proj.Projection",vc,OPENLAYERS);vc.prototype.getCode=vc.prototype.Sj;vc.prototype.getExtent=vc.prototype.H;vc.prototype.getUnits=vc.prototype.wb;vc.prototype.getMetersPerUnit=vc.prototype.$b;vc.prototype.getWorldExtent=vc.prototype.Ek;vc.prototype.isGlobal=vc.prototype.pl;vc.prototype.setGlobal=vc.prototype.ep;vc.prototype.setExtent=vc.prototype.Mm;vc.prototype.setWorldExtent=vc.prototype.mp; +vc.prototype.setGetPointResolution=vc.prototype.cp;vc.prototype.getPointResolution=vc.prototype.getPointResolution;t("ol.proj.setProj4",function(a){xc=a},OPENLAYERS);t("ol.proj.addEquivalentProjections",zc,OPENLAYERS);t("ol.proj.addProjection",Lc,OPENLAYERS);t("ol.proj.addCoordinateTransforms",Ac,OPENLAYERS);t("ol.proj.fromLonLat",function(a,b){return Rc(a,"EPSG:4326",void 0!==b?b:"EPSG:3857")},OPENLAYERS);t("ol.proj.toLonLat",function(a,b){return Rc(a,void 0!==b?b:"EPSG:3857","EPSG:4326")},OPENLAYERS); +t("ol.proj.get",yc,OPENLAYERS);t("ol.proj.equivalent",Oc,OPENLAYERS);t("ol.proj.getTransform",Pc,OPENLAYERS);t("ol.proj.transform",Rc,OPENLAYERS);t("ol.proj.transformExtent",Sc,OPENLAYERS);t("ol.layer.Heatmap",V,OPENLAYERS);V.prototype.getBlur=V.prototype.zg;V.prototype.getGradient=V.prototype.Gg;V.prototype.getRadius=V.prototype.ph;V.prototype.setBlur=V.prototype.di;V.prototype.setGradient=V.prototype.ii;V.prototype.setRadius=V.prototype.qh;t("ol.layer.Image",cj,OPENLAYERS); +cj.prototype.getSource=cj.prototype.ha;t("ol.layer.Layer",mh,OPENLAYERS);mh.prototype.getSource=mh.prototype.ha;mh.prototype.setMap=mh.prototype.setMap;mh.prototype.setSource=mh.prototype.Fc;t("ol.layer.Base",ih,OPENLAYERS);ih.prototype.getExtent=ih.prototype.H;ih.prototype.getMaxResolution=ih.prototype.Nb;ih.prototype.getMinResolution=ih.prototype.Ob;ih.prototype.getOpacity=ih.prototype.Pb;ih.prototype.getVisible=ih.prototype.xb;ih.prototype.getZIndex=ih.prototype.Qb;ih.prototype.setExtent=ih.prototype.fc; +ih.prototype.setMaxResolution=ih.prototype.nc;ih.prototype.setMinResolution=ih.prototype.oc;ih.prototype.setOpacity=ih.prototype.gc;ih.prototype.setVisible=ih.prototype.hc;ih.prototype.setZIndex=ih.prototype.ic;t("ol.layer.Group",Ti,OPENLAYERS);Ti.prototype.getLayers=Ti.prototype.Tc;Ti.prototype.setLayers=Ti.prototype.oh;t("ol.layer.Tile",dj,OPENLAYERS);dj.prototype.getPreload=dj.prototype.f;dj.prototype.getSource=dj.prototype.ha;dj.prototype.setPreload=dj.prototype.l; +dj.prototype.getUseInterimTilesOnError=dj.prototype.c;dj.prototype.setUseInterimTilesOnError=dj.prototype.A;t("ol.layer.Vector",G,OPENLAYERS);G.prototype.getSource=G.prototype.ha;G.prototype.getStyle=G.prototype.C;G.prototype.getStyleFunction=G.prototype.D;G.prototype.setStyle=G.prototype.l;t("ol.layer.VectorTile",I,OPENLAYERS);I.prototype.getPreload=I.prototype.f;I.prototype.getUseInterimTilesOnError=I.prototype.c;I.prototype.setPreload=I.prototype.Y;I.prototype.setUseInterimTilesOnError=I.prototype.ia; +t("ol.interaction.DoubleClickZoom",Zh,OPENLAYERS);t("ol.interaction.DoubleClickZoom.handleEvent",$h,OPENLAYERS);t("ol.interaction.DragAndDrop",hu,OPENLAYERS);t("ol.interaction.DragAndDrop.handleEvent",qc,OPENLAYERS);ku.prototype.features=ku.prototype.features;ku.prototype.file=ku.prototype.file;ku.prototype.projection=ku.prototype.projection;xi.prototype.coordinate=xi.prototype.coordinate;xi.prototype.mapBrowserEvent=xi.prototype.mapBrowserEvent;t("ol.interaction.DragBox",yi,OPENLAYERS); +yi.prototype.getGeometry=yi.prototype.W;t("ol.interaction.DragPan",mi,OPENLAYERS);t("ol.interaction.DragRotateAndZoom",mu,OPENLAYERS);t("ol.interaction.DragRotate",qi,OPENLAYERS);t("ol.interaction.DragZoom",Di,OPENLAYERS);qu.prototype.feature=qu.prototype.feature;t("ol.interaction.Draw",ru,OPENLAYERS);t("ol.interaction.Draw.handleEvent",tu,OPENLAYERS);ru.prototype.removeLastPoint=ru.prototype.Qo;ru.prototype.finishDrawing=ru.prototype.jd;ru.prototype.extend=ru.prototype.rm; +t("ol.interaction.Draw.createRegularPolygon",function(a,b){return function(c,d){var e=c[0],f=c[1],g=Math.sqrt(Hb(e,f)),h=d?d:Pd(new Vt(e),a);Qd(h,e,g,b?b:Math.atan((f[1]-e[1])/(f[0]-e[0])));return h}},OPENLAYERS);t("ol.interaction.Interaction",Vh,OPENLAYERS);Vh.prototype.getActive=Vh.prototype.f;Vh.prototype.getMap=Vh.prototype.l;Vh.prototype.setActive=Vh.prototype.i;t("ol.interaction.defaults",Si,OPENLAYERS);t("ol.interaction.KeyboardPan",Ei,OPENLAYERS); +t("ol.interaction.KeyboardPan.handleEvent",Fi,OPENLAYERS);t("ol.interaction.KeyboardZoom",Gi,OPENLAYERS);t("ol.interaction.KeyboardZoom.handleEvent",Hi,OPENLAYERS);Hu.prototype.features=Hu.prototype.features;Hu.prototype.mapBrowserEvent=Hu.prototype.mapBrowserEvent;t("ol.interaction.Modify",Iu,OPENLAYERS);t("ol.interaction.Modify.handleEvent",Lu,OPENLAYERS);Iu.prototype.removePoint=Iu.prototype.ai;t("ol.interaction.MouseWheelZoom",Ii,OPENLAYERS);t("ol.interaction.MouseWheelZoom.handleEvent",Ji,OPENLAYERS); +Ii.prototype.setMouseAnchor=Ii.prototype.D;t("ol.interaction.PinchRotate",Ki,OPENLAYERS);t("ol.interaction.PinchZoom",Oi,OPENLAYERS);t("ol.interaction.Pointer",ji,OPENLAYERS);t("ol.interaction.Pointer.handleEvent",ki,OPENLAYERS);Vu.prototype.selected=Vu.prototype.selected;Vu.prototype.deselected=Vu.prototype.deselected;Vu.prototype.mapBrowserEvent=Vu.prototype.mapBrowserEvent;t("ol.interaction.Select",Wu,OPENLAYERS);Wu.prototype.getFeatures=Wu.prototype.Bm;Wu.prototype.getLayer=Wu.prototype.Cm; +t("ol.interaction.Select.handleEvent",Xu,OPENLAYERS);Wu.prototype.setMap=Wu.prototype.setMap;t("ol.interaction.Snap",Zu,OPENLAYERS);Zu.prototype.addFeature=Zu.prototype.rb;Zu.prototype.removeFeature=Zu.prototype.nb;cv.prototype.features=cv.prototype.features;cv.prototype.coordinate=cv.prototype.coordinate;t("ol.interaction.Translate",dv,OPENLAYERS);t("ol.geom.Circle",Vt,OPENLAYERS);Vt.prototype.clone=Vt.prototype.clone;Vt.prototype.getCenter=Vt.prototype.rd;Vt.prototype.getRadius=Vt.prototype.wf; +Vt.prototype.getType=Vt.prototype.X;Vt.prototype.intersectsExtent=Vt.prototype.Ka;Vt.prototype.setCenter=Vt.prototype.jm;Vt.prototype.setCenterAndRadius=Vt.prototype.Vf;Vt.prototype.setRadius=Vt.prototype.km;Vt.prototype.transform=Vt.prototype.jb;t("ol.geom.Geometry",Tc,OPENLAYERS);Tc.prototype.getClosestPoint=Tc.prototype.vb;Tc.prototype.getExtent=Tc.prototype.H;Tc.prototype.rotate=Tc.prototype.rotate;Tc.prototype.simplify=Tc.prototype.Bb;Tc.prototype.transform=Tc.prototype.jb; +t("ol.geom.GeometryCollection",Ln,OPENLAYERS);Ln.prototype.clone=Ln.prototype.clone;Ln.prototype.getGeometries=Ln.prototype.ff;Ln.prototype.getType=Ln.prototype.X;Ln.prototype.intersectsExtent=Ln.prototype.Ka;Ln.prototype.setGeometries=Ln.prototype.hi;Ln.prototype.applyTransform=Ln.prototype.rc;Ln.prototype.translate=Ln.prototype.Sc;t("ol.geom.LinearRing",zd,OPENLAYERS);zd.prototype.clone=zd.prototype.clone;zd.prototype.getArea=zd.prototype.nm;zd.prototype.getCoordinates=zd.prototype.Z; +zd.prototype.getType=zd.prototype.X;zd.prototype.setCoordinates=zd.prototype.pa;t("ol.geom.LineString",R,OPENLAYERS);R.prototype.appendCoordinate=R.prototype.wj;R.prototype.clone=R.prototype.clone;R.prototype.forEachSegment=R.prototype.Lj;R.prototype.getCoordinateAtM=R.prototype.lm;R.prototype.getCoordinates=R.prototype.Z;R.prototype.getCoordinateAt=R.prototype.Bg;R.prototype.getLength=R.prototype.mm;R.prototype.getType=R.prototype.X;R.prototype.intersectsExtent=R.prototype.Ka; +R.prototype.setCoordinates=R.prototype.pa;t("ol.geom.MultiLineString",S,OPENLAYERS);S.prototype.appendLineString=S.prototype.xj;S.prototype.clone=S.prototype.clone;S.prototype.getCoordinateAtM=S.prototype.om;S.prototype.getCoordinates=S.prototype.Z;S.prototype.getLineString=S.prototype.fk;S.prototype.getLineStrings=S.prototype.md;S.prototype.getType=S.prototype.X;S.prototype.intersectsExtent=S.prototype.Ka;S.prototype.setCoordinates=S.prototype.pa;t("ol.geom.MultiPoint",Bn,OPENLAYERS); +Bn.prototype.appendPoint=Bn.prototype.zj;Bn.prototype.clone=Bn.prototype.clone;Bn.prototype.getCoordinates=Bn.prototype.Z;Bn.prototype.getPoint=Bn.prototype.rk;Bn.prototype.getPoints=Bn.prototype.je;Bn.prototype.getType=Bn.prototype.X;Bn.prototype.intersectsExtent=Bn.prototype.Ka;Bn.prototype.setCoordinates=Bn.prototype.pa;t("ol.geom.MultiPolygon",T,OPENLAYERS);T.prototype.appendPolygon=T.prototype.Aj;T.prototype.clone=T.prototype.clone;T.prototype.getArea=T.prototype.pm; +T.prototype.getCoordinates=T.prototype.Z;T.prototype.getInteriorPoints=T.prototype.ck;T.prototype.getPolygon=T.prototype.tk;T.prototype.getPolygons=T.prototype.Wd;T.prototype.getType=T.prototype.X;T.prototype.intersectsExtent=T.prototype.Ka;T.prototype.setCoordinates=T.prototype.pa;t("ol.geom.Point",C,OPENLAYERS);C.prototype.clone=C.prototype.clone;C.prototype.getCoordinates=C.prototype.Z;C.prototype.getType=C.prototype.X;C.prototype.intersectsExtent=C.prototype.Ka;C.prototype.setCoordinates=C.prototype.pa; +t("ol.geom.Polygon",E,OPENLAYERS);E.prototype.appendLinearRing=E.prototype.yj;E.prototype.clone=E.prototype.clone;E.prototype.getArea=E.prototype.qm;E.prototype.getCoordinates=E.prototype.Z;E.prototype.getInteriorPoint=E.prototype.bk;E.prototype.getLinearRingCount=E.prototype.gk;E.prototype.getLinearRing=E.prototype.Hg;E.prototype.getLinearRings=E.prototype.Vd;E.prototype.getType=E.prototype.X;E.prototype.intersectsExtent=E.prototype.Ka;E.prototype.setCoordinates=E.prototype.pa; +t("ol.geom.Polygon.circular",Nd,OPENLAYERS);t("ol.geom.Polygon.fromExtent",Od,OPENLAYERS);t("ol.geom.Polygon.fromCircle",Pd,OPENLAYERS);t("ol.geom.SimpleGeometry",hd,OPENLAYERS);hd.prototype.getFirstCoordinate=hd.prototype.Ib;hd.prototype.getLastCoordinate=hd.prototype.Jb;hd.prototype.getLayout=hd.prototype.Kb;hd.prototype.applyTransform=hd.prototype.rc;hd.prototype.translate=hd.prototype.Sc;t("ol.format.EsriJSON",En,OPENLAYERS);En.prototype.readFeature=En.prototype.Rb;En.prototype.readFeatures=En.prototype.Fa; +En.prototype.readGeometry=En.prototype.Vc;En.prototype.readProjection=En.prototype.Oa;En.prototype.writeGeometry=En.prototype.Zc;En.prototype.writeGeometryObject=En.prototype.Ke;En.prototype.writeFeature=En.prototype.Dd;En.prototype.writeFeatureObject=En.prototype.Yc;En.prototype.writeFeatures=En.prototype.Xb;En.prototype.writeFeaturesObject=En.prototype.Ie;t("ol.format.Feature",rn,OPENLAYERS);t("ol.format.GeoJSON",Pn,OPENLAYERS);Pn.prototype.readFeature=Pn.prototype.Rb; +Pn.prototype.readFeatures=Pn.prototype.Fa;Pn.prototype.readGeometry=Pn.prototype.Vc;Pn.prototype.readProjection=Pn.prototype.Oa;Pn.prototype.writeFeature=Pn.prototype.Dd;Pn.prototype.writeFeatureObject=Pn.prototype.Yc;Pn.prototype.writeFeatures=Pn.prototype.Xb;Pn.prototype.writeFeaturesObject=Pn.prototype.Ie;Pn.prototype.writeGeometry=Pn.prototype.Zc;Pn.prototype.writeGeometryObject=Pn.prototype.Ke;t("ol.format.GPX",uo,OPENLAYERS);uo.prototype.readFeature=uo.prototype.Rb; +uo.prototype.readFeatures=uo.prototype.Fa;uo.prototype.readProjection=uo.prototype.Oa;uo.prototype.writeFeatures=uo.prototype.Xb;uo.prototype.writeFeaturesNode=uo.prototype.a;t("ol.format.IGC",dp,OPENLAYERS);dp.prototype.readFeature=dp.prototype.Rb;dp.prototype.readFeatures=dp.prototype.Fa;dp.prototype.readProjection=dp.prototype.Oa;t("ol.format.KML",Hp,OPENLAYERS);Hp.prototype.readFeature=Hp.prototype.Rb;Hp.prototype.readFeatures=Hp.prototype.Fa;Hp.prototype.readName=Hp.prototype.Fo; +Hp.prototype.readNetworkLinks=Hp.prototype.Go;Hp.prototype.readProjection=Hp.prototype.Oa;Hp.prototype.writeFeatures=Hp.prototype.Xb;Hp.prototype.writeFeaturesNode=Hp.prototype.a;t("ol.format.MVT",wr,OPENLAYERS);wr.prototype.readFeatures=wr.prototype.Fa;wr.prototype.readProjection=wr.prototype.Oa;wr.prototype.setLayers=wr.prototype.c;t("ol.format.OSMXML",Sr,OPENLAYERS);Sr.prototype.readFeatures=Sr.prototype.Fa;Sr.prototype.readProjection=Sr.prototype.Oa;t("ol.format.Polyline",qs,OPENLAYERS); +t("ol.format.Polyline.encodeDeltas",rs,OPENLAYERS);t("ol.format.Polyline.decodeDeltas",ts,OPENLAYERS);t("ol.format.Polyline.encodeFloats",ss,OPENLAYERS);t("ol.format.Polyline.decodeFloats",us,OPENLAYERS);qs.prototype.readFeature=qs.prototype.Rb;qs.prototype.readFeatures=qs.prototype.Fa;qs.prototype.readGeometry=qs.prototype.Vc;qs.prototype.readProjection=qs.prototype.Oa;qs.prototype.writeGeometry=qs.prototype.Zc;t("ol.format.TopoJSON",ws,OPENLAYERS);ws.prototype.readFeatures=ws.prototype.Fa; +ws.prototype.readProjection=ws.prototype.Oa;t("ol.format.WFS",Cs,OPENLAYERS);Cs.prototype.readFeatures=Cs.prototype.Fa;Cs.prototype.readTransactionResponse=Cs.prototype.o;Cs.prototype.readFeatureCollectionMetadata=Cs.prototype.l;Cs.prototype.writeGetFeature=Cs.prototype.j;Cs.prototype.writeTransaction=Cs.prototype.U;Cs.prototype.readProjection=Cs.prototype.Oa;t("ol.format.WKT",Ts,OPENLAYERS);Ts.prototype.readFeature=Ts.prototype.Rb;Ts.prototype.readFeatures=Ts.prototype.Fa; +Ts.prototype.readGeometry=Ts.prototype.Vc;Ts.prototype.writeFeature=Ts.prototype.Dd;Ts.prototype.writeFeatures=Ts.prototype.Xb;Ts.prototype.writeGeometry=Ts.prototype.Zc;t("ol.format.WMSCapabilities",jt,OPENLAYERS);jt.prototype.read=jt.prototype.read;t("ol.format.WMSGetFeatureInfo",Gt,OPENLAYERS);Gt.prototype.readFeatures=Gt.prototype.Fa;t("ol.format.WMTSCapabilities",Ht,OPENLAYERS);Ht.prototype.read=Ht.prototype.read;t("ol.format.ogc.filter.and",yr,OPENLAYERS); +t("ol.format.ogc.filter.or",function(a,b){return new Fr(a,b)},OPENLAYERS);t("ol.format.ogc.filter.not",function(a){return new Gr(a)},OPENLAYERS);t("ol.format.ogc.filter.bbox",Ar,OPENLAYERS);t("ol.format.ogc.filter.equalTo",function(a,b,c){return new Jr(a,b,c)},OPENLAYERS);t("ol.format.ogc.filter.notEqualTo",function(a,b,c){return new Kr(a,b,c)},OPENLAYERS);t("ol.format.ogc.filter.lessThan",function(a,b){return new Lr(a,b)},OPENLAYERS); +t("ol.format.ogc.filter.lessThanOrEqualTo",function(a,b){return new Mr(a,b)},OPENLAYERS);t("ol.format.ogc.filter.greaterThan",function(a,b){return new Nr(a,b)},OPENLAYERS);t("ol.format.ogc.filter.greaterThanOrEqualTo",function(a,b){return new Or(a,b)},OPENLAYERS);t("ol.format.ogc.filter.isNull",function(a){return new Pr(a)},OPENLAYERS);t("ol.format.ogc.filter.between",function(a,b,c){return new Qr(a,b,c)},OPENLAYERS); +t("ol.format.ogc.filter.like",function(a,b,c,d,e,f){return new Rr(a,b,c,d,e,f)},OPENLAYERS);t("ol.format.ogc.filter.Filter",Cr,OPENLAYERS);t("ol.format.ogc.filter.And",zr,OPENLAYERS);t("ol.format.ogc.filter.Or",Fr,OPENLAYERS);t("ol.format.ogc.filter.Not",Gr,OPENLAYERS);t("ol.format.ogc.filter.Bbox",Br,OPENLAYERS);t("ol.format.ogc.filter.Comparison",Hr,OPENLAYERS);t("ol.format.ogc.filter.ComparisonBinary",Ir,OPENLAYERS);t("ol.format.ogc.filter.EqualTo",Jr,OPENLAYERS); +t("ol.format.ogc.filter.NotEqualTo",Kr,OPENLAYERS);t("ol.format.ogc.filter.LessThan",Lr,OPENLAYERS);t("ol.format.ogc.filter.LessThanOrEqualTo",Mr,OPENLAYERS);t("ol.format.ogc.filter.GreaterThan",Nr,OPENLAYERS);t("ol.format.ogc.filter.GreaterThanOrEqualTo",Or,OPENLAYERS);t("ol.format.ogc.filter.IsNull",Pr,OPENLAYERS);t("ol.format.ogc.filter.IsBetween",Qr,OPENLAYERS);t("ol.format.ogc.filter.IsLike",Rr,OPENLAYERS);t("ol.format.GML2",ko,OPENLAYERS);t("ol.format.GML3",lo,OPENLAYERS); +lo.prototype.writeGeometryNode=lo.prototype.s;lo.prototype.writeFeatures=lo.prototype.Xb;lo.prototype.writeFeaturesNode=lo.prototype.a;t("ol.format.GML",lo,OPENLAYERS);lo.prototype.writeFeatures=lo.prototype.Xb;lo.prototype.writeFeaturesNode=lo.prototype.a;Xn.prototype.readFeatures=Xn.prototype.Fa;t("ol.events.condition.altKeyOnly",function(a){a=a.originalEvent;return a.altKey&&!(a.metaKey||a.ctrlKey)&&!a.shiftKey},OPENLAYERS);t("ol.events.condition.altShiftKeysOnly",ai,OPENLAYERS); +t("ol.events.condition.always",qc,OPENLAYERS);t("ol.events.condition.click",function(a){return a.type==Zg},OPENLAYERS);t("ol.events.condition.never",rc,OPENLAYERS);t("ol.events.condition.pointerMove",ci,OPENLAYERS);t("ol.events.condition.singleClick",di,OPENLAYERS);t("ol.events.condition.doubleClick",function(a){return a.type==$g},OPENLAYERS);t("ol.events.condition.noModifierKeys",ei,OPENLAYERS); +t("ol.events.condition.platformModifierKeyOnly",function(a){a=a.originalEvent;return!a.altKey&&(fg?a.metaKey:a.ctrlKey)&&!a.shiftKey},OPENLAYERS);t("ol.events.condition.shiftKeyOnly",fi,OPENLAYERS);t("ol.events.condition.targetNotEditable",gi,OPENLAYERS);t("ol.events.condition.mouseOnly",hi,OPENLAYERS);t("ol.events.condition.primaryAction",ii,OPENLAYERS);Wa.prototype.type=Wa.prototype.type;Wa.prototype.target=Wa.prototype.target;Wa.prototype.preventDefault=Wa.prototype.preventDefault; +Wa.prototype.stopPropagation=Wa.prototype.stopPropagation;t("ol.control.Attribution",Ef,OPENLAYERS);t("ol.control.Attribution.render",Ff,OPENLAYERS);Ef.prototype.getCollapsible=Ef.prototype.$l;Ef.prototype.setCollapsible=Ef.prototype.cm;Ef.prototype.setCollapsed=Ef.prototype.bm;Ef.prototype.getCollapsed=Ef.prototype.Zl;t("ol.control.Control",Xe,OPENLAYERS);Xe.prototype.getMap=Xe.prototype.i;Xe.prototype.setMap=Xe.prototype.setMap;Xe.prototype.setTarget=Xe.prototype.c;t("ol.control.defaults",Kf,OPENLAYERS); +t("ol.control.FullScreen",Lf,OPENLAYERS);t("ol.control.MousePosition",Qf,OPENLAYERS);t("ol.control.MousePosition.render",Rf,OPENLAYERS);Qf.prototype.getCoordinateFormat=Qf.prototype.Cg;Qf.prototype.getProjection=Qf.prototype.hh;Qf.prototype.setCoordinateFormat=Qf.prototype.ei;Qf.prototype.setProjection=Qf.prototype.ih;t("ol.control.OverviewMap",an,OPENLAYERS);t("ol.control.OverviewMap.render",bn,OPENLAYERS);an.prototype.getCollapsible=an.prototype.fm;an.prototype.setCollapsible=an.prototype.im; +an.prototype.setCollapsed=an.prototype.hm;an.prototype.getCollapsed=an.prototype.em;an.prototype.getOverviewMap=an.prototype.pk;t("ol.control.Rotate",Hf,OPENLAYERS);t("ol.control.Rotate.render",If,OPENLAYERS);t("ol.control.ScaleLine",fn,OPENLAYERS);fn.prototype.getUnits=fn.prototype.wb;t("ol.control.ScaleLine.render",gn,OPENLAYERS);fn.prototype.setUnits=fn.prototype.D;t("ol.control.Zoom",Jf,OPENLAYERS);t("ol.control.ZoomSlider",kn,OPENLAYERS);t("ol.control.ZoomSlider.render",mn,OPENLAYERS); +t("ol.control.ZoomToExtent",pn,OPENLAYERS);t("ol.color.asArray",te,OPENLAYERS);t("ol.color.asString",ve,OPENLAYERS);ke.prototype.type=ke.prototype.type;ke.prototype.target=ke.prototype.target;ke.prototype.preventDefault=ke.prototype.preventDefault;ke.prototype.stopPropagation=ke.prototype.stopPropagation;eb.prototype.changed=eb.prototype.u;eb.prototype.dispatchEvent=eb.prototype.b;eb.prototype.getRevision=eb.prototype.K;eb.prototype.on=eb.prototype.I;eb.prototype.once=eb.prototype.L; +eb.prototype.un=eb.prototype.J;eb.prototype.unByKey=eb.prototype.M;le.prototype.get=le.prototype.get;le.prototype.getKeys=le.prototype.N;le.prototype.getProperties=le.prototype.O;le.prototype.set=le.prototype.set;le.prototype.setProperties=le.prototype.G;le.prototype.unset=le.prototype.P;le.prototype.changed=le.prototype.u;le.prototype.dispatchEvent=le.prototype.b;le.prototype.getRevision=le.prototype.K;le.prototype.on=le.prototype.I;le.prototype.once=le.prototype.L;le.prototype.un=le.prototype.J; +le.prototype.unByKey=le.prototype.M;qn.prototype.get=qn.prototype.get;qn.prototype.getKeys=qn.prototype.N;qn.prototype.getProperties=qn.prototype.O;qn.prototype.set=qn.prototype.set;qn.prototype.setProperties=qn.prototype.G;qn.prototype.unset=qn.prototype.P;qn.prototype.changed=qn.prototype.u;qn.prototype.dispatchEvent=qn.prototype.b;qn.prototype.getRevision=qn.prototype.K;qn.prototype.on=qn.prototype.I;qn.prototype.once=qn.prototype.L;qn.prototype.un=qn.prototype.J;qn.prototype.unByKey=qn.prototype.M; +Ik.prototype.get=Ik.prototype.get;Ik.prototype.getKeys=Ik.prototype.N;Ik.prototype.getProperties=Ik.prototype.O;Ik.prototype.set=Ik.prototype.set;Ik.prototype.setProperties=Ik.prototype.G;Ik.prototype.unset=Ik.prototype.P;Ik.prototype.changed=Ik.prototype.u;Ik.prototype.dispatchEvent=Ik.prototype.b;Ik.prototype.getRevision=Ik.prototype.K;Ik.prototype.on=Ik.prototype.I;Ik.prototype.once=Ik.prototype.L;Ik.prototype.un=Ik.prototype.J;Ik.prototype.unByKey=Ik.prototype.M;Ut.prototype.get=Ut.prototype.get; +Ut.prototype.getKeys=Ut.prototype.N;Ut.prototype.getProperties=Ut.prototype.O;Ut.prototype.set=Ut.prototype.set;Ut.prototype.setProperties=Ut.prototype.G;Ut.prototype.unset=Ut.prototype.P;Ut.prototype.changed=Ut.prototype.u;Ut.prototype.dispatchEvent=Ut.prototype.b;Ut.prototype.getRevision=Ut.prototype.K;Ut.prototype.on=Ut.prototype.I;Ut.prototype.once=Ut.prototype.L;Ut.prototype.un=Ut.prototype.J;Ut.prototype.unByKey=Ut.prototype.M;fu.prototype.getTileCoord=fu.prototype.i;Q.prototype.get=Q.prototype.get; +Q.prototype.getKeys=Q.prototype.N;Q.prototype.getProperties=Q.prototype.O;Q.prototype.set=Q.prototype.set;Q.prototype.setProperties=Q.prototype.G;Q.prototype.unset=Q.prototype.P;Q.prototype.changed=Q.prototype.u;Q.prototype.dispatchEvent=Q.prototype.b;Q.prototype.getRevision=Q.prototype.K;Q.prototype.on=Q.prototype.I;Q.prototype.once=Q.prototype.L;Q.prototype.un=Q.prototype.J;Q.prototype.unByKey=Q.prototype.M;We.prototype.type=We.prototype.type;We.prototype.target=We.prototype.target; +We.prototype.preventDefault=We.prototype.preventDefault;We.prototype.stopPropagation=We.prototype.stopPropagation;Vg.prototype.map=Vg.prototype.map;Vg.prototype.frameState=Vg.prototype.frameState;Vg.prototype.type=Vg.prototype.type;Vg.prototype.target=Vg.prototype.target;Vg.prototype.preventDefault=Vg.prototype.preventDefault;Vg.prototype.stopPropagation=Vg.prototype.stopPropagation;Wg.prototype.originalEvent=Wg.prototype.originalEvent;Wg.prototype.pixel=Wg.prototype.pixel; +Wg.prototype.coordinate=Wg.prototype.coordinate;Wg.prototype.dragging=Wg.prototype.dragging;Wg.prototype.preventDefault=Wg.prototype.preventDefault;Wg.prototype.stopPropagation=Wg.prototype.stopPropagation;Wg.prototype.map=Wg.prototype.map;Wg.prototype.frameState=Wg.prototype.frameState;Wg.prototype.type=Wg.prototype.type;Wg.prototype.target=Wg.prototype.target;db.prototype.type=db.prototype.type;db.prototype.target=db.prototype.target;db.prototype.preventDefault=db.prototype.preventDefault; +db.prototype.stopPropagation=db.prototype.stopPropagation;Xm.prototype.get=Xm.prototype.get;Xm.prototype.getKeys=Xm.prototype.N;Xm.prototype.getProperties=Xm.prototype.O;Xm.prototype.set=Xm.prototype.set;Xm.prototype.setProperties=Xm.prototype.G;Xm.prototype.unset=Xm.prototype.P;Xm.prototype.changed=Xm.prototype.u;Xm.prototype.dispatchEvent=Xm.prototype.b;Xm.prototype.getRevision=Xm.prototype.K;Xm.prototype.on=Xm.prototype.I;Xm.prototype.once=Xm.prototype.L;Xm.prototype.un=Xm.prototype.J; +Xm.prototype.unByKey=Xm.prototype.M;Kk.prototype.getTileCoord=Kk.prototype.i;Rd.prototype.get=Rd.prototype.get;Rd.prototype.getKeys=Rd.prototype.N;Rd.prototype.getProperties=Rd.prototype.O;Rd.prototype.set=Rd.prototype.set;Rd.prototype.setProperties=Rd.prototype.G;Rd.prototype.unset=Rd.prototype.P;Rd.prototype.changed=Rd.prototype.u;Rd.prototype.dispatchEvent=Rd.prototype.b;Rd.prototype.getRevision=Rd.prototype.K;Rd.prototype.on=Rd.prototype.I;Rd.prototype.once=Rd.prototype.L;Rd.prototype.un=Rd.prototype.J; +Rd.prototype.unByKey=Rd.prototype.M;fw.prototype.forEachTileCoord=fw.prototype.yg;fw.prototype.getMaxZoom=fw.prototype.Ig;fw.prototype.getMinZoom=fw.prototype.Jg;fw.prototype.getOrigin=fw.prototype.Ia;fw.prototype.getResolution=fw.prototype.$;fw.prototype.getResolutions=fw.prototype.Kh;fw.prototype.getTileCoordExtent=fw.prototype.Ea;fw.prototype.getTileCoordForCoordAndResolution=fw.prototype.Zd;fw.prototype.getTileCoordForCoordAndZ=fw.prototype.qd;fw.prototype.getTileSize=fw.prototype.Ja; +fw.prototype.getZForResolution=fw.prototype.Lb;qj.prototype.getOpacity=qj.prototype.qe;qj.prototype.getRotateWithView=qj.prototype.Xd;qj.prototype.getRotation=qj.prototype.re;qj.prototype.getScale=qj.prototype.se;qj.prototype.getSnapToPixel=qj.prototype.Yd;qj.prototype.setOpacity=qj.prototype.te;qj.prototype.setRotation=qj.prototype.ue;qj.prototype.setScale=qj.prototype.ve;Dh.prototype.getOpacity=Dh.prototype.qe;Dh.prototype.getRotateWithView=Dh.prototype.Xd;Dh.prototype.getRotation=Dh.prototype.re; +Dh.prototype.getScale=Dh.prototype.se;Dh.prototype.getSnapToPixel=Dh.prototype.Yd;Dh.prototype.setOpacity=Dh.prototype.te;Dh.prototype.setRotation=Dh.prototype.ue;Dh.prototype.setScale=Dh.prototype.ve;ow.prototype.getOpacity=ow.prototype.qe;ow.prototype.getRotateWithView=ow.prototype.Xd;ow.prototype.getRotation=ow.prototype.re;ow.prototype.getScale=ow.prototype.se;ow.prototype.getSnapToPixel=ow.prototype.Yd;ow.prototype.setOpacity=ow.prototype.te;ow.prototype.setRotation=ow.prototype.ue; +ow.prototype.setScale=ow.prototype.ve;jf.prototype.get=jf.prototype.get;jf.prototype.getKeys=jf.prototype.N;jf.prototype.getProperties=jf.prototype.O;jf.prototype.set=jf.prototype.set;jf.prototype.setProperties=jf.prototype.G;jf.prototype.unset=jf.prototype.P;jf.prototype.changed=jf.prototype.u;jf.prototype.dispatchEvent=jf.prototype.b;jf.prototype.getRevision=jf.prototype.K;jf.prototype.on=jf.prototype.I;jf.prototype.once=jf.prototype.L;jf.prototype.un=jf.prototype.J;jf.prototype.unByKey=jf.prototype.M; +zf.prototype.getAttributions=zf.prototype.wa;zf.prototype.getLogo=zf.prototype.ua;zf.prototype.getProjection=zf.prototype.xa;zf.prototype.getState=zf.prototype.V;zf.prototype.refresh=zf.prototype.sa;zf.prototype.setAttributions=zf.prototype.oa;zf.prototype.get=zf.prototype.get;zf.prototype.getKeys=zf.prototype.N;zf.prototype.getProperties=zf.prototype.O;zf.prototype.set=zf.prototype.set;zf.prototype.setProperties=zf.prototype.G;zf.prototype.unset=zf.prototype.P;zf.prototype.changed=zf.prototype.u; +zf.prototype.dispatchEvent=zf.prototype.b;zf.prototype.getRevision=zf.prototype.K;zf.prototype.on=zf.prototype.I;zf.prototype.once=zf.prototype.L;zf.prototype.un=zf.prototype.J;zf.prototype.unByKey=zf.prototype.M;Jl.prototype.getTileGrid=Jl.prototype.Na;Jl.prototype.refresh=Jl.prototype.sa;Jl.prototype.getAttributions=Jl.prototype.wa;Jl.prototype.getLogo=Jl.prototype.ua;Jl.prototype.getProjection=Jl.prototype.xa;Jl.prototype.getState=Jl.prototype.V;Jl.prototype.setAttributions=Jl.prototype.oa; +Jl.prototype.get=Jl.prototype.get;Jl.prototype.getKeys=Jl.prototype.N;Jl.prototype.getProperties=Jl.prototype.O;Jl.prototype.set=Jl.prototype.set;Jl.prototype.setProperties=Jl.prototype.G;Jl.prototype.unset=Jl.prototype.P;Jl.prototype.changed=Jl.prototype.u;Jl.prototype.dispatchEvent=Jl.prototype.b;Jl.prototype.getRevision=Jl.prototype.K;Jl.prototype.on=Jl.prototype.I;Jl.prototype.once=Jl.prototype.L;Jl.prototype.un=Jl.prototype.J;Jl.prototype.unByKey=Jl.prototype.M; +W.prototype.getTileLoadFunction=W.prototype.fb;W.prototype.getTileUrlFunction=W.prototype.gb;W.prototype.getUrls=W.prototype.hb;W.prototype.setTileLoadFunction=W.prototype.kb;W.prototype.setTileUrlFunction=W.prototype.Qa;W.prototype.setUrl=W.prototype.Va;W.prototype.setUrls=W.prototype.bb;W.prototype.getTileGrid=W.prototype.Na;W.prototype.refresh=W.prototype.sa;W.prototype.getAttributions=W.prototype.wa;W.prototype.getLogo=W.prototype.ua;W.prototype.getProjection=W.prototype.xa; +W.prototype.getState=W.prototype.V;W.prototype.setAttributions=W.prototype.oa;W.prototype.get=W.prototype.get;W.prototype.getKeys=W.prototype.N;W.prototype.getProperties=W.prototype.O;W.prototype.set=W.prototype.set;W.prototype.setProperties=W.prototype.G;W.prototype.unset=W.prototype.P;W.prototype.changed=W.prototype.u;W.prototype.dispatchEvent=W.prototype.b;W.prototype.getRevision=W.prototype.K;W.prototype.on=W.prototype.I;W.prototype.once=W.prototype.L;W.prototype.un=W.prototype.J; +W.prototype.unByKey=W.prototype.M;pv.prototype.setRenderReprojectionEdges=pv.prototype.zb;pv.prototype.setTileGridForProjection=pv.prototype.Ab;pv.prototype.getTileLoadFunction=pv.prototype.fb;pv.prototype.getTileUrlFunction=pv.prototype.gb;pv.prototype.getUrls=pv.prototype.hb;pv.prototype.setTileLoadFunction=pv.prototype.kb;pv.prototype.setTileUrlFunction=pv.prototype.Qa;pv.prototype.setUrl=pv.prototype.Va;pv.prototype.setUrls=pv.prototype.bb;pv.prototype.getTileGrid=pv.prototype.Na; +pv.prototype.refresh=pv.prototype.sa;pv.prototype.getAttributions=pv.prototype.wa;pv.prototype.getLogo=pv.prototype.ua;pv.prototype.getProjection=pv.prototype.xa;pv.prototype.getState=pv.prototype.V;pv.prototype.setAttributions=pv.prototype.oa;pv.prototype.get=pv.prototype.get;pv.prototype.getKeys=pv.prototype.N;pv.prototype.getProperties=pv.prototype.O;pv.prototype.set=pv.prototype.set;pv.prototype.setProperties=pv.prototype.G;pv.prototype.unset=pv.prototype.P;pv.prototype.changed=pv.prototype.u; +pv.prototype.dispatchEvent=pv.prototype.b;pv.prototype.getRevision=pv.prototype.K;pv.prototype.on=pv.prototype.I;pv.prototype.once=pv.prototype.L;pv.prototype.un=pv.prototype.J;pv.prototype.unByKey=pv.prototype.M;rv.prototype.setRenderReprojectionEdges=rv.prototype.zb;rv.prototype.setTileGridForProjection=rv.prototype.Ab;rv.prototype.getTileLoadFunction=rv.prototype.fb;rv.prototype.getTileUrlFunction=rv.prototype.gb;rv.prototype.getUrls=rv.prototype.hb;rv.prototype.setTileLoadFunction=rv.prototype.kb; +rv.prototype.setTileUrlFunction=rv.prototype.Qa;rv.prototype.setUrl=rv.prototype.Va;rv.prototype.setUrls=rv.prototype.bb;rv.prototype.getTileGrid=rv.prototype.Na;rv.prototype.refresh=rv.prototype.sa;rv.prototype.getAttributions=rv.prototype.wa;rv.prototype.getLogo=rv.prototype.ua;rv.prototype.getProjection=rv.prototype.xa;rv.prototype.getState=rv.prototype.V;rv.prototype.setAttributions=rv.prototype.oa;rv.prototype.get=rv.prototype.get;rv.prototype.getKeys=rv.prototype.N; +rv.prototype.getProperties=rv.prototype.O;rv.prototype.set=rv.prototype.set;rv.prototype.setProperties=rv.prototype.G;rv.prototype.unset=rv.prototype.P;rv.prototype.changed=rv.prototype.u;rv.prototype.dispatchEvent=rv.prototype.b;rv.prototype.getRevision=rv.prototype.K;rv.prototype.on=rv.prototype.I;rv.prototype.once=rv.prototype.L;rv.prototype.un=rv.prototype.J;rv.prototype.unByKey=rv.prototype.M;sv.prototype.setRenderReprojectionEdges=sv.prototype.zb;sv.prototype.setTileGridForProjection=sv.prototype.Ab; +sv.prototype.getTileLoadFunction=sv.prototype.fb;sv.prototype.getTileUrlFunction=sv.prototype.gb;sv.prototype.getUrls=sv.prototype.hb;sv.prototype.setTileLoadFunction=sv.prototype.kb;sv.prototype.setTileUrlFunction=sv.prototype.Qa;sv.prototype.setUrl=sv.prototype.Va;sv.prototype.setUrls=sv.prototype.bb;sv.prototype.getTileGrid=sv.prototype.Na;sv.prototype.refresh=sv.prototype.sa;sv.prototype.getAttributions=sv.prototype.wa;sv.prototype.getLogo=sv.prototype.ua;sv.prototype.getProjection=sv.prototype.xa; +sv.prototype.getState=sv.prototype.V;sv.prototype.setAttributions=sv.prototype.oa;sv.prototype.get=sv.prototype.get;sv.prototype.getKeys=sv.prototype.N;sv.prototype.getProperties=sv.prototype.O;sv.prototype.set=sv.prototype.set;sv.prototype.setProperties=sv.prototype.G;sv.prototype.unset=sv.prototype.P;sv.prototype.changed=sv.prototype.u;sv.prototype.dispatchEvent=sv.prototype.b;sv.prototype.getRevision=sv.prototype.K;sv.prototype.on=sv.prototype.I;sv.prototype.once=sv.prototype.L; +sv.prototype.un=sv.prototype.J;sv.prototype.unByKey=sv.prototype.M;P.prototype.getAttributions=P.prototype.wa;P.prototype.getLogo=P.prototype.ua;P.prototype.getProjection=P.prototype.xa;P.prototype.getState=P.prototype.V;P.prototype.refresh=P.prototype.sa;P.prototype.setAttributions=P.prototype.oa;P.prototype.get=P.prototype.get;P.prototype.getKeys=P.prototype.N;P.prototype.getProperties=P.prototype.O;P.prototype.set=P.prototype.set;P.prototype.setProperties=P.prototype.G;P.prototype.unset=P.prototype.P; +P.prototype.changed=P.prototype.u;P.prototype.dispatchEvent=P.prototype.b;P.prototype.getRevision=P.prototype.K;P.prototype.on=P.prototype.I;P.prototype.once=P.prototype.L;P.prototype.un=P.prototype.J;P.prototype.unByKey=P.prototype.M;Y.prototype.addFeature=Y.prototype.rb;Y.prototype.addFeatures=Y.prototype.Jc;Y.prototype.clear=Y.prototype.clear;Y.prototype.forEachFeature=Y.prototype.wg;Y.prototype.forEachFeatureInExtent=Y.prototype.ub;Y.prototype.forEachFeatureIntersectingExtent=Y.prototype.xg; +Y.prototype.getFeaturesCollection=Y.prototype.Fg;Y.prototype.getFeatures=Y.prototype.oe;Y.prototype.getFeaturesAtCoordinate=Y.prototype.Eg;Y.prototype.getFeaturesInExtent=Y.prototype.ef;Y.prototype.getClosestFeatureToCoordinate=Y.prototype.Ag;Y.prototype.getExtent=Y.prototype.H;Y.prototype.getFeatureById=Y.prototype.Dg;Y.prototype.getFormat=Y.prototype.Ch;Y.prototype.getUrl=Y.prototype.Dh;Y.prototype.removeFeature=Y.prototype.nb;Y.prototype.getAttributions=Y.prototype.wa;Y.prototype.getLogo=Y.prototype.ua; +Y.prototype.getProjection=Y.prototype.xa;Y.prototype.getState=Y.prototype.V;Y.prototype.refresh=Y.prototype.sa;Y.prototype.setAttributions=Y.prototype.oa;Y.prototype.get=Y.prototype.get;Y.prototype.getKeys=Y.prototype.N;Y.prototype.getProperties=Y.prototype.O;Y.prototype.set=Y.prototype.set;Y.prototype.setProperties=Y.prototype.G;Y.prototype.unset=Y.prototype.P;Y.prototype.changed=Y.prototype.u;Y.prototype.dispatchEvent=Y.prototype.b;Y.prototype.getRevision=Y.prototype.K;Y.prototype.on=Y.prototype.I; +Y.prototype.once=Y.prototype.L;Y.prototype.un=Y.prototype.J;Y.prototype.unByKey=Y.prototype.M;Ak.prototype.getAttributions=Ak.prototype.wa;Ak.prototype.getLogo=Ak.prototype.ua;Ak.prototype.getProjection=Ak.prototype.xa;Ak.prototype.getState=Ak.prototype.V;Ak.prototype.refresh=Ak.prototype.sa;Ak.prototype.setAttributions=Ak.prototype.oa;Ak.prototype.get=Ak.prototype.get;Ak.prototype.getKeys=Ak.prototype.N;Ak.prototype.getProperties=Ak.prototype.O;Ak.prototype.set=Ak.prototype.set; +Ak.prototype.setProperties=Ak.prototype.G;Ak.prototype.unset=Ak.prototype.P;Ak.prototype.changed=Ak.prototype.u;Ak.prototype.dispatchEvent=Ak.prototype.b;Ak.prototype.getRevision=Ak.prototype.K;Ak.prototype.on=Ak.prototype.I;Ak.prototype.once=Ak.prototype.L;Ak.prototype.un=Ak.prototype.J;Ak.prototype.unByKey=Ak.prototype.M;yv.prototype.getAttributions=yv.prototype.wa;yv.prototype.getLogo=yv.prototype.ua;yv.prototype.getProjection=yv.prototype.xa;yv.prototype.getState=yv.prototype.V; +yv.prototype.refresh=yv.prototype.sa;yv.prototype.setAttributions=yv.prototype.oa;yv.prototype.get=yv.prototype.get;yv.prototype.getKeys=yv.prototype.N;yv.prototype.getProperties=yv.prototype.O;yv.prototype.set=yv.prototype.set;yv.prototype.setProperties=yv.prototype.G;yv.prototype.unset=yv.prototype.P;yv.prototype.changed=yv.prototype.u;yv.prototype.dispatchEvent=yv.prototype.b;yv.prototype.getRevision=yv.prototype.K;yv.prototype.on=yv.prototype.I;yv.prototype.once=yv.prototype.L; +yv.prototype.un=yv.prototype.J;yv.prototype.unByKey=yv.prototype.M;Hk.prototype.getAttributions=Hk.prototype.wa;Hk.prototype.getLogo=Hk.prototype.ua;Hk.prototype.getProjection=Hk.prototype.xa;Hk.prototype.getState=Hk.prototype.V;Hk.prototype.refresh=Hk.prototype.sa;Hk.prototype.setAttributions=Hk.prototype.oa;Hk.prototype.get=Hk.prototype.get;Hk.prototype.getKeys=Hk.prototype.N;Hk.prototype.getProperties=Hk.prototype.O;Hk.prototype.set=Hk.prototype.set;Hk.prototype.setProperties=Hk.prototype.G; +Hk.prototype.unset=Hk.prototype.P;Hk.prototype.changed=Hk.prototype.u;Hk.prototype.dispatchEvent=Hk.prototype.b;Hk.prototype.getRevision=Hk.prototype.K;Hk.prototype.on=Hk.prototype.I;Hk.prototype.once=Hk.prototype.L;Hk.prototype.un=Hk.prototype.J;Hk.prototype.unByKey=Hk.prototype.M;zv.prototype.getAttributions=zv.prototype.wa;zv.prototype.getLogo=zv.prototype.ua;zv.prototype.getProjection=zv.prototype.xa;zv.prototype.getState=zv.prototype.V;zv.prototype.refresh=zv.prototype.sa; +zv.prototype.setAttributions=zv.prototype.oa;zv.prototype.get=zv.prototype.get;zv.prototype.getKeys=zv.prototype.N;zv.prototype.getProperties=zv.prototype.O;zv.prototype.set=zv.prototype.set;zv.prototype.setProperties=zv.prototype.G;zv.prototype.unset=zv.prototype.P;zv.prototype.changed=zv.prototype.u;zv.prototype.dispatchEvent=zv.prototype.b;zv.prototype.getRevision=zv.prototype.K;zv.prototype.on=zv.prototype.I;zv.prototype.once=zv.prototype.L;zv.prototype.un=zv.prototype.J; +zv.prototype.unByKey=zv.prototype.M;Ck.prototype.type=Ck.prototype.type;Ck.prototype.target=Ck.prototype.target;Ck.prototype.preventDefault=Ck.prototype.preventDefault;Ck.prototype.stopPropagation=Ck.prototype.stopPropagation;Av.prototype.getAttributions=Av.prototype.wa;Av.prototype.getLogo=Av.prototype.ua;Av.prototype.getProjection=Av.prototype.xa;Av.prototype.getState=Av.prototype.V;Av.prototype.refresh=Av.prototype.sa;Av.prototype.setAttributions=Av.prototype.oa;Av.prototype.get=Av.prototype.get; +Av.prototype.getKeys=Av.prototype.N;Av.prototype.getProperties=Av.prototype.O;Av.prototype.set=Av.prototype.set;Av.prototype.setProperties=Av.prototype.G;Av.prototype.unset=Av.prototype.P;Av.prototype.changed=Av.prototype.u;Av.prototype.dispatchEvent=Av.prototype.b;Av.prototype.getRevision=Av.prototype.K;Av.prototype.on=Av.prototype.I;Av.prototype.once=Av.prototype.L;Av.prototype.un=Av.prototype.J;Av.prototype.unByKey=Av.prototype.M;yl.prototype.getAttributions=yl.prototype.wa; +yl.prototype.getLogo=yl.prototype.ua;yl.prototype.getProjection=yl.prototype.xa;yl.prototype.getState=yl.prototype.V;yl.prototype.refresh=yl.prototype.sa;yl.prototype.setAttributions=yl.prototype.oa;yl.prototype.get=yl.prototype.get;yl.prototype.getKeys=yl.prototype.N;yl.prototype.getProperties=yl.prototype.O;yl.prototype.set=yl.prototype.set;yl.prototype.setProperties=yl.prototype.G;yl.prototype.unset=yl.prototype.P;yl.prototype.changed=yl.prototype.u;yl.prototype.dispatchEvent=yl.prototype.b; +yl.prototype.getRevision=yl.prototype.K;yl.prototype.on=yl.prototype.I;yl.prototype.once=yl.prototype.L;yl.prototype.un=yl.prototype.J;yl.prototype.unByKey=yl.prototype.M;Bv.prototype.getAttributions=Bv.prototype.wa;Bv.prototype.getLogo=Bv.prototype.ua;Bv.prototype.getProjection=Bv.prototype.xa;Bv.prototype.getState=Bv.prototype.V;Bv.prototype.refresh=Bv.prototype.sa;Bv.prototype.setAttributions=Bv.prototype.oa;Bv.prototype.get=Bv.prototype.get;Bv.prototype.getKeys=Bv.prototype.N; +Bv.prototype.getProperties=Bv.prototype.O;Bv.prototype.set=Bv.prototype.set;Bv.prototype.setProperties=Bv.prototype.G;Bv.prototype.unset=Bv.prototype.P;Bv.prototype.changed=Bv.prototype.u;Bv.prototype.dispatchEvent=Bv.prototype.b;Bv.prototype.getRevision=Bv.prototype.K;Bv.prototype.on=Bv.prototype.I;Bv.prototype.once=Bv.prototype.L;Bv.prototype.un=Bv.prototype.J;Bv.prototype.unByKey=Bv.prototype.M;Fv.prototype.setRenderReprojectionEdges=Fv.prototype.zb;Fv.prototype.setTileGridForProjection=Fv.prototype.Ab; +Fv.prototype.getTileLoadFunction=Fv.prototype.fb;Fv.prototype.getTileUrlFunction=Fv.prototype.gb;Fv.prototype.getUrls=Fv.prototype.hb;Fv.prototype.setTileLoadFunction=Fv.prototype.kb;Fv.prototype.setTileUrlFunction=Fv.prototype.Qa;Fv.prototype.setUrl=Fv.prototype.Va;Fv.prototype.setUrls=Fv.prototype.bb;Fv.prototype.getTileGrid=Fv.prototype.Na;Fv.prototype.refresh=Fv.prototype.sa;Fv.prototype.getAttributions=Fv.prototype.wa;Fv.prototype.getLogo=Fv.prototype.ua;Fv.prototype.getProjection=Fv.prototype.xa; +Fv.prototype.getState=Fv.prototype.V;Fv.prototype.setAttributions=Fv.prototype.oa;Fv.prototype.get=Fv.prototype.get;Fv.prototype.getKeys=Fv.prototype.N;Fv.prototype.getProperties=Fv.prototype.O;Fv.prototype.set=Fv.prototype.set;Fv.prototype.setProperties=Fv.prototype.G;Fv.prototype.unset=Fv.prototype.P;Fv.prototype.changed=Fv.prototype.u;Fv.prototype.dispatchEvent=Fv.prototype.b;Fv.prototype.getRevision=Fv.prototype.K;Fv.prototype.on=Fv.prototype.I;Fv.prototype.once=Fv.prototype.L; +Fv.prototype.un=Fv.prototype.J;Fv.prototype.unByKey=Fv.prototype.M;Hv.prototype.getAttributions=Hv.prototype.wa;Hv.prototype.getLogo=Hv.prototype.ua;Hv.prototype.getProjection=Hv.prototype.xa;Hv.prototype.getState=Hv.prototype.V;Hv.prototype.refresh=Hv.prototype.sa;Hv.prototype.setAttributions=Hv.prototype.oa;Hv.prototype.get=Hv.prototype.get;Hv.prototype.getKeys=Hv.prototype.N;Hv.prototype.getProperties=Hv.prototype.O;Hv.prototype.set=Hv.prototype.set;Hv.prototype.setProperties=Hv.prototype.G; +Hv.prototype.unset=Hv.prototype.P;Hv.prototype.changed=Hv.prototype.u;Hv.prototype.dispatchEvent=Hv.prototype.b;Hv.prototype.getRevision=Hv.prototype.K;Hv.prototype.on=Hv.prototype.I;Hv.prototype.once=Hv.prototype.L;Hv.prototype.un=Hv.prototype.J;Hv.prototype.unByKey=Hv.prototype.M;Mv.prototype.type=Mv.prototype.type;Mv.prototype.target=Mv.prototype.target;Mv.prototype.preventDefault=Mv.prototype.preventDefault;Mv.prototype.stopPropagation=Mv.prototype.stopPropagation; +Rv.prototype.setRenderReprojectionEdges=Rv.prototype.zb;Rv.prototype.setTileGridForProjection=Rv.prototype.Ab;Rv.prototype.getTileLoadFunction=Rv.prototype.fb;Rv.prototype.getTileUrlFunction=Rv.prototype.gb;Rv.prototype.getUrls=Rv.prototype.hb;Rv.prototype.setTileLoadFunction=Rv.prototype.kb;Rv.prototype.setTileUrlFunction=Rv.prototype.Qa;Rv.prototype.setUrl=Rv.prototype.Va;Rv.prototype.setUrls=Rv.prototype.bb;Rv.prototype.getTileGrid=Rv.prototype.Na;Rv.prototype.refresh=Rv.prototype.sa; +Rv.prototype.getAttributions=Rv.prototype.wa;Rv.prototype.getLogo=Rv.prototype.ua;Rv.prototype.getProjection=Rv.prototype.xa;Rv.prototype.getState=Rv.prototype.V;Rv.prototype.setAttributions=Rv.prototype.oa;Rv.prototype.get=Rv.prototype.get;Rv.prototype.getKeys=Rv.prototype.N;Rv.prototype.getProperties=Rv.prototype.O;Rv.prototype.set=Rv.prototype.set;Rv.prototype.setProperties=Rv.prototype.G;Rv.prototype.unset=Rv.prototype.P;Rv.prototype.changed=Rv.prototype.u;Rv.prototype.dispatchEvent=Rv.prototype.b; +Rv.prototype.getRevision=Rv.prototype.K;Rv.prototype.on=Rv.prototype.I;Rv.prototype.once=Rv.prototype.L;Rv.prototype.un=Rv.prototype.J;Rv.prototype.unByKey=Rv.prototype.M;Tv.prototype.setRenderReprojectionEdges=Tv.prototype.zb;Tv.prototype.setTileGridForProjection=Tv.prototype.Ab;Tv.prototype.getTileLoadFunction=Tv.prototype.fb;Tv.prototype.getTileUrlFunction=Tv.prototype.gb;Tv.prototype.getUrls=Tv.prototype.hb;Tv.prototype.setTileLoadFunction=Tv.prototype.kb;Tv.prototype.setTileUrlFunction=Tv.prototype.Qa; +Tv.prototype.setUrl=Tv.prototype.Va;Tv.prototype.setUrls=Tv.prototype.bb;Tv.prototype.getTileGrid=Tv.prototype.Na;Tv.prototype.refresh=Tv.prototype.sa;Tv.prototype.getAttributions=Tv.prototype.wa;Tv.prototype.getLogo=Tv.prototype.ua;Tv.prototype.getProjection=Tv.prototype.xa;Tv.prototype.getState=Tv.prototype.V;Tv.prototype.setAttributions=Tv.prototype.oa;Tv.prototype.get=Tv.prototype.get;Tv.prototype.getKeys=Tv.prototype.N;Tv.prototype.getProperties=Tv.prototype.O;Tv.prototype.set=Tv.prototype.set; +Tv.prototype.setProperties=Tv.prototype.G;Tv.prototype.unset=Tv.prototype.P;Tv.prototype.changed=Tv.prototype.u;Tv.prototype.dispatchEvent=Tv.prototype.b;Tv.prototype.getRevision=Tv.prototype.K;Tv.prototype.on=Tv.prototype.I;Tv.prototype.once=Tv.prototype.L;Tv.prototype.un=Tv.prototype.J;Tv.prototype.unByKey=Tv.prototype.M;Vv.prototype.getTileGrid=Vv.prototype.Na;Vv.prototype.refresh=Vv.prototype.sa;Vv.prototype.getAttributions=Vv.prototype.wa;Vv.prototype.getLogo=Vv.prototype.ua; +Vv.prototype.getProjection=Vv.prototype.xa;Vv.prototype.getState=Vv.prototype.V;Vv.prototype.setAttributions=Vv.prototype.oa;Vv.prototype.get=Vv.prototype.get;Vv.prototype.getKeys=Vv.prototype.N;Vv.prototype.getProperties=Vv.prototype.O;Vv.prototype.set=Vv.prototype.set;Vv.prototype.setProperties=Vv.prototype.G;Vv.prototype.unset=Vv.prototype.P;Vv.prototype.changed=Vv.prototype.u;Vv.prototype.dispatchEvent=Vv.prototype.b;Vv.prototype.getRevision=Vv.prototype.K;Vv.prototype.on=Vv.prototype.I; +Vv.prototype.once=Vv.prototype.L;Vv.prototype.un=Vv.prototype.J;Vv.prototype.unByKey=Vv.prototype.M;Wv.prototype.setRenderReprojectionEdges=Wv.prototype.zb;Wv.prototype.setTileGridForProjection=Wv.prototype.Ab;Wv.prototype.getTileLoadFunction=Wv.prototype.fb;Wv.prototype.getTileUrlFunction=Wv.prototype.gb;Wv.prototype.getUrls=Wv.prototype.hb;Wv.prototype.setTileLoadFunction=Wv.prototype.kb;Wv.prototype.setTileUrlFunction=Wv.prototype.Qa;Wv.prototype.setUrl=Wv.prototype.Va;Wv.prototype.setUrls=Wv.prototype.bb; +Wv.prototype.getTileGrid=Wv.prototype.Na;Wv.prototype.refresh=Wv.prototype.sa;Wv.prototype.getAttributions=Wv.prototype.wa;Wv.prototype.getLogo=Wv.prototype.ua;Wv.prototype.getProjection=Wv.prototype.xa;Wv.prototype.getState=Wv.prototype.V;Wv.prototype.setAttributions=Wv.prototype.oa;Wv.prototype.get=Wv.prototype.get;Wv.prototype.getKeys=Wv.prototype.N;Wv.prototype.getProperties=Wv.prototype.O;Wv.prototype.set=Wv.prototype.set;Wv.prototype.setProperties=Wv.prototype.G;Wv.prototype.unset=Wv.prototype.P; +Wv.prototype.changed=Wv.prototype.u;Wv.prototype.dispatchEvent=Wv.prototype.b;Wv.prototype.getRevision=Wv.prototype.K;Wv.prototype.on=Wv.prototype.I;Wv.prototype.once=Wv.prototype.L;Wv.prototype.un=Wv.prototype.J;Wv.prototype.unByKey=Wv.prototype.M;Df.prototype.type=Df.prototype.type;Df.prototype.target=Df.prototype.target;Df.prototype.preventDefault=Df.prototype.preventDefault;Df.prototype.stopPropagation=Df.prototype.stopPropagation;Xv.prototype.getTileGrid=Xv.prototype.Na; +Xv.prototype.refresh=Xv.prototype.sa;Xv.prototype.getAttributions=Xv.prototype.wa;Xv.prototype.getLogo=Xv.prototype.ua;Xv.prototype.getProjection=Xv.prototype.xa;Xv.prototype.getState=Xv.prototype.V;Xv.prototype.setAttributions=Xv.prototype.oa;Xv.prototype.get=Xv.prototype.get;Xv.prototype.getKeys=Xv.prototype.N;Xv.prototype.getProperties=Xv.prototype.O;Xv.prototype.set=Xv.prototype.set;Xv.prototype.setProperties=Xv.prototype.G;Xv.prototype.unset=Xv.prototype.P;Xv.prototype.changed=Xv.prototype.u; +Xv.prototype.dispatchEvent=Xv.prototype.b;Xv.prototype.getRevision=Xv.prototype.K;Xv.prototype.on=Xv.prototype.I;Xv.prototype.once=Xv.prototype.L;Xv.prototype.un=Xv.prototype.J;Xv.prototype.unByKey=Xv.prototype.M;aw.prototype.setRenderReprojectionEdges=aw.prototype.zb;aw.prototype.setTileGridForProjection=aw.prototype.Ab;aw.prototype.getTileLoadFunction=aw.prototype.fb;aw.prototype.getTileUrlFunction=aw.prototype.gb;aw.prototype.getUrls=aw.prototype.hb;aw.prototype.setTileLoadFunction=aw.prototype.kb; +aw.prototype.setTileUrlFunction=aw.prototype.Qa;aw.prototype.setUrl=aw.prototype.Va;aw.prototype.setUrls=aw.prototype.bb;aw.prototype.getTileGrid=aw.prototype.Na;aw.prototype.refresh=aw.prototype.sa;aw.prototype.getAttributions=aw.prototype.wa;aw.prototype.getLogo=aw.prototype.ua;aw.prototype.getProjection=aw.prototype.xa;aw.prototype.getState=aw.prototype.V;aw.prototype.setAttributions=aw.prototype.oa;aw.prototype.get=aw.prototype.get;aw.prototype.getKeys=aw.prototype.N; +aw.prototype.getProperties=aw.prototype.O;aw.prototype.set=aw.prototype.set;aw.prototype.setProperties=aw.prototype.G;aw.prototype.unset=aw.prototype.P;aw.prototype.changed=aw.prototype.u;aw.prototype.dispatchEvent=aw.prototype.b;aw.prototype.getRevision=aw.prototype.K;aw.prototype.on=aw.prototype.I;aw.prototype.once=aw.prototype.L;aw.prototype.un=aw.prototype.J;aw.prototype.unByKey=aw.prototype.M;vl.prototype.type=vl.prototype.type;vl.prototype.target=vl.prototype.target; +vl.prototype.preventDefault=vl.prototype.preventDefault;vl.prototype.stopPropagation=vl.prototype.stopPropagation;Kl.prototype.getTileLoadFunction=Kl.prototype.fb;Kl.prototype.getTileUrlFunction=Kl.prototype.gb;Kl.prototype.getUrls=Kl.prototype.hb;Kl.prototype.setTileLoadFunction=Kl.prototype.kb;Kl.prototype.setTileUrlFunction=Kl.prototype.Qa;Kl.prototype.setUrl=Kl.prototype.Va;Kl.prototype.setUrls=Kl.prototype.bb;Kl.prototype.getTileGrid=Kl.prototype.Na;Kl.prototype.refresh=Kl.prototype.sa; +Kl.prototype.getAttributions=Kl.prototype.wa;Kl.prototype.getLogo=Kl.prototype.ua;Kl.prototype.getProjection=Kl.prototype.xa;Kl.prototype.getState=Kl.prototype.V;Kl.prototype.setAttributions=Kl.prototype.oa;Kl.prototype.get=Kl.prototype.get;Kl.prototype.getKeys=Kl.prototype.N;Kl.prototype.getProperties=Kl.prototype.O;Kl.prototype.set=Kl.prototype.set;Kl.prototype.setProperties=Kl.prototype.G;Kl.prototype.unset=Kl.prototype.P;Kl.prototype.changed=Kl.prototype.u;Kl.prototype.dispatchEvent=Kl.prototype.b; +Kl.prototype.getRevision=Kl.prototype.K;Kl.prototype.on=Kl.prototype.I;Kl.prototype.once=Kl.prototype.L;Kl.prototype.un=Kl.prototype.J;Kl.prototype.unByKey=Kl.prototype.M;Z.prototype.setRenderReprojectionEdges=Z.prototype.zb;Z.prototype.setTileGridForProjection=Z.prototype.Ab;Z.prototype.getTileLoadFunction=Z.prototype.fb;Z.prototype.getTileUrlFunction=Z.prototype.gb;Z.prototype.getUrls=Z.prototype.hb;Z.prototype.setTileLoadFunction=Z.prototype.kb;Z.prototype.setTileUrlFunction=Z.prototype.Qa; +Z.prototype.setUrl=Z.prototype.Va;Z.prototype.setUrls=Z.prototype.bb;Z.prototype.getTileGrid=Z.prototype.Na;Z.prototype.refresh=Z.prototype.sa;Z.prototype.getAttributions=Z.prototype.wa;Z.prototype.getLogo=Z.prototype.ua;Z.prototype.getProjection=Z.prototype.xa;Z.prototype.getState=Z.prototype.V;Z.prototype.setAttributions=Z.prototype.oa;Z.prototype.get=Z.prototype.get;Z.prototype.getKeys=Z.prototype.N;Z.prototype.getProperties=Z.prototype.O;Z.prototype.set=Z.prototype.set; +Z.prototype.setProperties=Z.prototype.G;Z.prototype.unset=Z.prototype.P;Z.prototype.changed=Z.prototype.u;Z.prototype.dispatchEvent=Z.prototype.b;Z.prototype.getRevision=Z.prototype.K;Z.prototype.on=Z.prototype.I;Z.prototype.once=Z.prototype.L;Z.prototype.un=Z.prototype.J;Z.prototype.unByKey=Z.prototype.M;iw.prototype.setRenderReprojectionEdges=iw.prototype.zb;iw.prototype.setTileGridForProjection=iw.prototype.Ab;iw.prototype.getTileLoadFunction=iw.prototype.fb;iw.prototype.getTileUrlFunction=iw.prototype.gb; +iw.prototype.getUrls=iw.prototype.hb;iw.prototype.setTileLoadFunction=iw.prototype.kb;iw.prototype.setTileUrlFunction=iw.prototype.Qa;iw.prototype.setUrl=iw.prototype.Va;iw.prototype.setUrls=iw.prototype.bb;iw.prototype.getTileGrid=iw.prototype.Na;iw.prototype.refresh=iw.prototype.sa;iw.prototype.getAttributions=iw.prototype.wa;iw.prototype.getLogo=iw.prototype.ua;iw.prototype.getProjection=iw.prototype.xa;iw.prototype.getState=iw.prototype.V;iw.prototype.setAttributions=iw.prototype.oa; +iw.prototype.get=iw.prototype.get;iw.prototype.getKeys=iw.prototype.N;iw.prototype.getProperties=iw.prototype.O;iw.prototype.set=iw.prototype.set;iw.prototype.setProperties=iw.prototype.G;iw.prototype.unset=iw.prototype.P;iw.prototype.changed=iw.prototype.u;iw.prototype.dispatchEvent=iw.prototype.b;iw.prototype.getRevision=iw.prototype.K;iw.prototype.on=iw.prototype.I;iw.prototype.once=iw.prototype.L;iw.prototype.un=iw.prototype.J;iw.prototype.unByKey=iw.prototype.M;lv.prototype.getTileCoord=lv.prototype.i; +lv.prototype.load=lv.prototype.load;th.prototype.changed=th.prototype.u;th.prototype.dispatchEvent=th.prototype.b;th.prototype.getRevision=th.prototype.K;th.prototype.on=th.prototype.I;th.prototype.once=th.prototype.L;th.prototype.un=th.prototype.J;th.prototype.unByKey=th.prototype.M;Gm.prototype.changed=Gm.prototype.u;Gm.prototype.dispatchEvent=Gm.prototype.b;Gm.prototype.getRevision=Gm.prototype.K;Gm.prototype.on=Gm.prototype.I;Gm.prototype.once=Gm.prototype.L;Gm.prototype.un=Gm.prototype.J; +Gm.prototype.unByKey=Gm.prototype.M;Jm.prototype.changed=Jm.prototype.u;Jm.prototype.dispatchEvent=Jm.prototype.b;Jm.prototype.getRevision=Jm.prototype.K;Jm.prototype.on=Jm.prototype.I;Jm.prototype.once=Jm.prototype.L;Jm.prototype.un=Jm.prototype.J;Jm.prototype.unByKey=Jm.prototype.M;Pm.prototype.changed=Pm.prototype.u;Pm.prototype.dispatchEvent=Pm.prototype.b;Pm.prototype.getRevision=Pm.prototype.K;Pm.prototype.on=Pm.prototype.I;Pm.prototype.once=Pm.prototype.L;Pm.prototype.un=Pm.prototype.J; +Pm.prototype.unByKey=Pm.prototype.M;Rm.prototype.changed=Rm.prototype.u;Rm.prototype.dispatchEvent=Rm.prototype.b;Rm.prototype.getRevision=Rm.prototype.K;Rm.prototype.on=Rm.prototype.I;Rm.prototype.once=Rm.prototype.L;Rm.prototype.un=Rm.prototype.J;Rm.prototype.unByKey=Rm.prototype.M;Sl.prototype.changed=Sl.prototype.u;Sl.prototype.dispatchEvent=Sl.prototype.b;Sl.prototype.getRevision=Sl.prototype.K;Sl.prototype.on=Sl.prototype.I;Sl.prototype.once=Sl.prototype.L;Sl.prototype.un=Sl.prototype.J; +Sl.prototype.unByKey=Sl.prototype.M;Tl.prototype.changed=Tl.prototype.u;Tl.prototype.dispatchEvent=Tl.prototype.b;Tl.prototype.getRevision=Tl.prototype.K;Tl.prototype.on=Tl.prototype.I;Tl.prototype.once=Tl.prototype.L;Tl.prototype.un=Tl.prototype.J;Tl.prototype.unByKey=Tl.prototype.M;Ul.prototype.changed=Ul.prototype.u;Ul.prototype.dispatchEvent=Ul.prototype.b;Ul.prototype.getRevision=Ul.prototype.K;Ul.prototype.on=Ul.prototype.I;Ul.prototype.once=Ul.prototype.L;Ul.prototype.un=Ul.prototype.J; +Ul.prototype.unByKey=Ul.prototype.M;Wl.prototype.changed=Wl.prototype.u;Wl.prototype.dispatchEvent=Wl.prototype.b;Wl.prototype.getRevision=Wl.prototype.K;Wl.prototype.on=Wl.prototype.I;Wl.prototype.once=Wl.prototype.L;Wl.prototype.un=Wl.prototype.J;Wl.prototype.unByKey=Wl.prototype.M;Jj.prototype.changed=Jj.prototype.u;Jj.prototype.dispatchEvent=Jj.prototype.b;Jj.prototype.getRevision=Jj.prototype.K;Jj.prototype.on=Jj.prototype.I;Jj.prototype.once=Jj.prototype.L;Jj.prototype.un=Jj.prototype.J; +Jj.prototype.unByKey=Jj.prototype.M;Al.prototype.changed=Al.prototype.u;Al.prototype.dispatchEvent=Al.prototype.b;Al.prototype.getRevision=Al.prototype.K;Al.prototype.on=Al.prototype.I;Al.prototype.once=Al.prototype.L;Al.prototype.un=Al.prototype.J;Al.prototype.unByKey=Al.prototype.M;Bl.prototype.changed=Bl.prototype.u;Bl.prototype.dispatchEvent=Bl.prototype.b;Bl.prototype.getRevision=Bl.prototype.K;Bl.prototype.on=Bl.prototype.I;Bl.prototype.once=Bl.prototype.L;Bl.prototype.un=Bl.prototype.J; +Bl.prototype.unByKey=Bl.prototype.M;Dl.prototype.changed=Dl.prototype.u;Dl.prototype.dispatchEvent=Dl.prototype.b;Dl.prototype.getRevision=Dl.prototype.K;Dl.prototype.on=Dl.prototype.I;Dl.prototype.once=Dl.prototype.L;Dl.prototype.un=Dl.prototype.J;Dl.prototype.unByKey=Dl.prototype.M;Ol.prototype.changed=Ol.prototype.u;Ol.prototype.dispatchEvent=Ol.prototype.b;Ol.prototype.getRevision=Ol.prototype.K;Ol.prototype.on=Ol.prototype.I;Ol.prototype.once=Ol.prototype.L;Ol.prototype.un=Ol.prototype.J; +Ol.prototype.unByKey=Ol.prototype.M;lh.prototype.type=lh.prototype.type;lh.prototype.target=lh.prototype.target;lh.prototype.preventDefault=lh.prototype.preventDefault;lh.prototype.stopPropagation=lh.prototype.stopPropagation;Wf.prototype.type=Wf.prototype.type;Wf.prototype.target=Wf.prototype.target;Wf.prototype.preventDefault=Wf.prototype.preventDefault;Wf.prototype.stopPropagation=Wf.prototype.stopPropagation;ih.prototype.get=ih.prototype.get;ih.prototype.getKeys=ih.prototype.N; +ih.prototype.getProperties=ih.prototype.O;ih.prototype.set=ih.prototype.set;ih.prototype.setProperties=ih.prototype.G;ih.prototype.unset=ih.prototype.P;ih.prototype.changed=ih.prototype.u;ih.prototype.dispatchEvent=ih.prototype.b;ih.prototype.getRevision=ih.prototype.K;ih.prototype.on=ih.prototype.I;ih.prototype.once=ih.prototype.L;ih.prototype.un=ih.prototype.J;ih.prototype.unByKey=ih.prototype.M;mh.prototype.getExtent=mh.prototype.H;mh.prototype.getMaxResolution=mh.prototype.Nb; +mh.prototype.getMinResolution=mh.prototype.Ob;mh.prototype.getOpacity=mh.prototype.Pb;mh.prototype.getVisible=mh.prototype.xb;mh.prototype.getZIndex=mh.prototype.Qb;mh.prototype.setExtent=mh.prototype.fc;mh.prototype.setMaxResolution=mh.prototype.nc;mh.prototype.setMinResolution=mh.prototype.oc;mh.prototype.setOpacity=mh.prototype.gc;mh.prototype.setVisible=mh.prototype.hc;mh.prototype.setZIndex=mh.prototype.ic;mh.prototype.get=mh.prototype.get;mh.prototype.getKeys=mh.prototype.N; +mh.prototype.getProperties=mh.prototype.O;mh.prototype.set=mh.prototype.set;mh.prototype.setProperties=mh.prototype.G;mh.prototype.unset=mh.prototype.P;mh.prototype.changed=mh.prototype.u;mh.prototype.dispatchEvent=mh.prototype.b;mh.prototype.getRevision=mh.prototype.K;mh.prototype.on=mh.prototype.I;mh.prototype.once=mh.prototype.L;mh.prototype.un=mh.prototype.J;mh.prototype.unByKey=mh.prototype.M;G.prototype.setMap=G.prototype.setMap;G.prototype.setSource=G.prototype.Fc;G.prototype.getExtent=G.prototype.H; +G.prototype.getMaxResolution=G.prototype.Nb;G.prototype.getMinResolution=G.prototype.Ob;G.prototype.getOpacity=G.prototype.Pb;G.prototype.getVisible=G.prototype.xb;G.prototype.getZIndex=G.prototype.Qb;G.prototype.setExtent=G.prototype.fc;G.prototype.setMaxResolution=G.prototype.nc;G.prototype.setMinResolution=G.prototype.oc;G.prototype.setOpacity=G.prototype.gc;G.prototype.setVisible=G.prototype.hc;G.prototype.setZIndex=G.prototype.ic;G.prototype.get=G.prototype.get;G.prototype.getKeys=G.prototype.N; +G.prototype.getProperties=G.prototype.O;G.prototype.set=G.prototype.set;G.prototype.setProperties=G.prototype.G;G.prototype.unset=G.prototype.P;G.prototype.changed=G.prototype.u;G.prototype.dispatchEvent=G.prototype.b;G.prototype.getRevision=G.prototype.K;G.prototype.on=G.prototype.I;G.prototype.once=G.prototype.L;G.prototype.un=G.prototype.J;G.prototype.unByKey=G.prototype.M;V.prototype.getSource=V.prototype.ha;V.prototype.getStyle=V.prototype.C;V.prototype.getStyleFunction=V.prototype.D; +V.prototype.setStyle=V.prototype.l;V.prototype.setMap=V.prototype.setMap;V.prototype.setSource=V.prototype.Fc;V.prototype.getExtent=V.prototype.H;V.prototype.getMaxResolution=V.prototype.Nb;V.prototype.getMinResolution=V.prototype.Ob;V.prototype.getOpacity=V.prototype.Pb;V.prototype.getVisible=V.prototype.xb;V.prototype.getZIndex=V.prototype.Qb;V.prototype.setExtent=V.prototype.fc;V.prototype.setMaxResolution=V.prototype.nc;V.prototype.setMinResolution=V.prototype.oc;V.prototype.setOpacity=V.prototype.gc; +V.prototype.setVisible=V.prototype.hc;V.prototype.setZIndex=V.prototype.ic;V.prototype.get=V.prototype.get;V.prototype.getKeys=V.prototype.N;V.prototype.getProperties=V.prototype.O;V.prototype.set=V.prototype.set;V.prototype.setProperties=V.prototype.G;V.prototype.unset=V.prototype.P;V.prototype.changed=V.prototype.u;V.prototype.dispatchEvent=V.prototype.b;V.prototype.getRevision=V.prototype.K;V.prototype.on=V.prototype.I;V.prototype.once=V.prototype.L;V.prototype.un=V.prototype.J; +V.prototype.unByKey=V.prototype.M;cj.prototype.setMap=cj.prototype.setMap;cj.prototype.setSource=cj.prototype.Fc;cj.prototype.getExtent=cj.prototype.H;cj.prototype.getMaxResolution=cj.prototype.Nb;cj.prototype.getMinResolution=cj.prototype.Ob;cj.prototype.getOpacity=cj.prototype.Pb;cj.prototype.getVisible=cj.prototype.xb;cj.prototype.getZIndex=cj.prototype.Qb;cj.prototype.setExtent=cj.prototype.fc;cj.prototype.setMaxResolution=cj.prototype.nc;cj.prototype.setMinResolution=cj.prototype.oc; +cj.prototype.setOpacity=cj.prototype.gc;cj.prototype.setVisible=cj.prototype.hc;cj.prototype.setZIndex=cj.prototype.ic;cj.prototype.get=cj.prototype.get;cj.prototype.getKeys=cj.prototype.N;cj.prototype.getProperties=cj.prototype.O;cj.prototype.set=cj.prototype.set;cj.prototype.setProperties=cj.prototype.G;cj.prototype.unset=cj.prototype.P;cj.prototype.changed=cj.prototype.u;cj.prototype.dispatchEvent=cj.prototype.b;cj.prototype.getRevision=cj.prototype.K;cj.prototype.on=cj.prototype.I; +cj.prototype.once=cj.prototype.L;cj.prototype.un=cj.prototype.J;cj.prototype.unByKey=cj.prototype.M;Ti.prototype.getExtent=Ti.prototype.H;Ti.prototype.getMaxResolution=Ti.prototype.Nb;Ti.prototype.getMinResolution=Ti.prototype.Ob;Ti.prototype.getOpacity=Ti.prototype.Pb;Ti.prototype.getVisible=Ti.prototype.xb;Ti.prototype.getZIndex=Ti.prototype.Qb;Ti.prototype.setExtent=Ti.prototype.fc;Ti.prototype.setMaxResolution=Ti.prototype.nc;Ti.prototype.setMinResolution=Ti.prototype.oc; +Ti.prototype.setOpacity=Ti.prototype.gc;Ti.prototype.setVisible=Ti.prototype.hc;Ti.prototype.setZIndex=Ti.prototype.ic;Ti.prototype.get=Ti.prototype.get;Ti.prototype.getKeys=Ti.prototype.N;Ti.prototype.getProperties=Ti.prototype.O;Ti.prototype.set=Ti.prototype.set;Ti.prototype.setProperties=Ti.prototype.G;Ti.prototype.unset=Ti.prototype.P;Ti.prototype.changed=Ti.prototype.u;Ti.prototype.dispatchEvent=Ti.prototype.b;Ti.prototype.getRevision=Ti.prototype.K;Ti.prototype.on=Ti.prototype.I; +Ti.prototype.once=Ti.prototype.L;Ti.prototype.un=Ti.prototype.J;Ti.prototype.unByKey=Ti.prototype.M;dj.prototype.setMap=dj.prototype.setMap;dj.prototype.setSource=dj.prototype.Fc;dj.prototype.getExtent=dj.prototype.H;dj.prototype.getMaxResolution=dj.prototype.Nb;dj.prototype.getMinResolution=dj.prototype.Ob;dj.prototype.getOpacity=dj.prototype.Pb;dj.prototype.getVisible=dj.prototype.xb;dj.prototype.getZIndex=dj.prototype.Qb;dj.prototype.setExtent=dj.prototype.fc;dj.prototype.setMaxResolution=dj.prototype.nc; +dj.prototype.setMinResolution=dj.prototype.oc;dj.prototype.setOpacity=dj.prototype.gc;dj.prototype.setVisible=dj.prototype.hc;dj.prototype.setZIndex=dj.prototype.ic;dj.prototype.get=dj.prototype.get;dj.prototype.getKeys=dj.prototype.N;dj.prototype.getProperties=dj.prototype.O;dj.prototype.set=dj.prototype.set;dj.prototype.setProperties=dj.prototype.G;dj.prototype.unset=dj.prototype.P;dj.prototype.changed=dj.prototype.u;dj.prototype.dispatchEvent=dj.prototype.b;dj.prototype.getRevision=dj.prototype.K; +dj.prototype.on=dj.prototype.I;dj.prototype.once=dj.prototype.L;dj.prototype.un=dj.prototype.J;dj.prototype.unByKey=dj.prototype.M;I.prototype.getSource=I.prototype.ha;I.prototype.getStyle=I.prototype.C;I.prototype.getStyleFunction=I.prototype.D;I.prototype.setStyle=I.prototype.l;I.prototype.setMap=I.prototype.setMap;I.prototype.setSource=I.prototype.Fc;I.prototype.getExtent=I.prototype.H;I.prototype.getMaxResolution=I.prototype.Nb;I.prototype.getMinResolution=I.prototype.Ob; +I.prototype.getOpacity=I.prototype.Pb;I.prototype.getVisible=I.prototype.xb;I.prototype.getZIndex=I.prototype.Qb;I.prototype.setExtent=I.prototype.fc;I.prototype.setMaxResolution=I.prototype.nc;I.prototype.setMinResolution=I.prototype.oc;I.prototype.setOpacity=I.prototype.gc;I.prototype.setVisible=I.prototype.hc;I.prototype.setZIndex=I.prototype.ic;I.prototype.get=I.prototype.get;I.prototype.getKeys=I.prototype.N;I.prototype.getProperties=I.prototype.O;I.prototype.set=I.prototype.set; +I.prototype.setProperties=I.prototype.G;I.prototype.unset=I.prototype.P;I.prototype.changed=I.prototype.u;I.prototype.dispatchEvent=I.prototype.b;I.prototype.getRevision=I.prototype.K;I.prototype.on=I.prototype.I;I.prototype.once=I.prototype.L;I.prototype.un=I.prototype.J;I.prototype.unByKey=I.prototype.M;Vh.prototype.get=Vh.prototype.get;Vh.prototype.getKeys=Vh.prototype.N;Vh.prototype.getProperties=Vh.prototype.O;Vh.prototype.set=Vh.prototype.set;Vh.prototype.setProperties=Vh.prototype.G; +Vh.prototype.unset=Vh.prototype.P;Vh.prototype.changed=Vh.prototype.u;Vh.prototype.dispatchEvent=Vh.prototype.b;Vh.prototype.getRevision=Vh.prototype.K;Vh.prototype.on=Vh.prototype.I;Vh.prototype.once=Vh.prototype.L;Vh.prototype.un=Vh.prototype.J;Vh.prototype.unByKey=Vh.prototype.M;Zh.prototype.getActive=Zh.prototype.f;Zh.prototype.getMap=Zh.prototype.l;Zh.prototype.setActive=Zh.prototype.i;Zh.prototype.get=Zh.prototype.get;Zh.prototype.getKeys=Zh.prototype.N;Zh.prototype.getProperties=Zh.prototype.O; +Zh.prototype.set=Zh.prototype.set;Zh.prototype.setProperties=Zh.prototype.G;Zh.prototype.unset=Zh.prototype.P;Zh.prototype.changed=Zh.prototype.u;Zh.prototype.dispatchEvent=Zh.prototype.b;Zh.prototype.getRevision=Zh.prototype.K;Zh.prototype.on=Zh.prototype.I;Zh.prototype.once=Zh.prototype.L;Zh.prototype.un=Zh.prototype.J;Zh.prototype.unByKey=Zh.prototype.M;hu.prototype.getActive=hu.prototype.f;hu.prototype.getMap=hu.prototype.l;hu.prototype.setActive=hu.prototype.i;hu.prototype.get=hu.prototype.get; +hu.prototype.getKeys=hu.prototype.N;hu.prototype.getProperties=hu.prototype.O;hu.prototype.set=hu.prototype.set;hu.prototype.setProperties=hu.prototype.G;hu.prototype.unset=hu.prototype.P;hu.prototype.changed=hu.prototype.u;hu.prototype.dispatchEvent=hu.prototype.b;hu.prototype.getRevision=hu.prototype.K;hu.prototype.on=hu.prototype.I;hu.prototype.once=hu.prototype.L;hu.prototype.un=hu.prototype.J;hu.prototype.unByKey=hu.prototype.M;ku.prototype.type=ku.prototype.type;ku.prototype.target=ku.prototype.target; +ku.prototype.preventDefault=ku.prototype.preventDefault;ku.prototype.stopPropagation=ku.prototype.stopPropagation;xi.prototype.type=xi.prototype.type;xi.prototype.target=xi.prototype.target;xi.prototype.preventDefault=xi.prototype.preventDefault;xi.prototype.stopPropagation=xi.prototype.stopPropagation;ji.prototype.getActive=ji.prototype.f;ji.prototype.getMap=ji.prototype.l;ji.prototype.setActive=ji.prototype.i;ji.prototype.get=ji.prototype.get;ji.prototype.getKeys=ji.prototype.N; +ji.prototype.getProperties=ji.prototype.O;ji.prototype.set=ji.prototype.set;ji.prototype.setProperties=ji.prototype.G;ji.prototype.unset=ji.prototype.P;ji.prototype.changed=ji.prototype.u;ji.prototype.dispatchEvent=ji.prototype.b;ji.prototype.getRevision=ji.prototype.K;ji.prototype.on=ji.prototype.I;ji.prototype.once=ji.prototype.L;ji.prototype.un=ji.prototype.J;ji.prototype.unByKey=ji.prototype.M;yi.prototype.getActive=yi.prototype.f;yi.prototype.getMap=yi.prototype.l;yi.prototype.setActive=yi.prototype.i; +yi.prototype.get=yi.prototype.get;yi.prototype.getKeys=yi.prototype.N;yi.prototype.getProperties=yi.prototype.O;yi.prototype.set=yi.prototype.set;yi.prototype.setProperties=yi.prototype.G;yi.prototype.unset=yi.prototype.P;yi.prototype.changed=yi.prototype.u;yi.prototype.dispatchEvent=yi.prototype.b;yi.prototype.getRevision=yi.prototype.K;yi.prototype.on=yi.prototype.I;yi.prototype.once=yi.prototype.L;yi.prototype.un=yi.prototype.J;yi.prototype.unByKey=yi.prototype.M;mi.prototype.getActive=mi.prototype.f; +mi.prototype.getMap=mi.prototype.l;mi.prototype.setActive=mi.prototype.i;mi.prototype.get=mi.prototype.get;mi.prototype.getKeys=mi.prototype.N;mi.prototype.getProperties=mi.prototype.O;mi.prototype.set=mi.prototype.set;mi.prototype.setProperties=mi.prototype.G;mi.prototype.unset=mi.prototype.P;mi.prototype.changed=mi.prototype.u;mi.prototype.dispatchEvent=mi.prototype.b;mi.prototype.getRevision=mi.prototype.K;mi.prototype.on=mi.prototype.I;mi.prototype.once=mi.prototype.L;mi.prototype.un=mi.prototype.J; +mi.prototype.unByKey=mi.prototype.M;mu.prototype.getActive=mu.prototype.f;mu.prototype.getMap=mu.prototype.l;mu.prototype.setActive=mu.prototype.i;mu.prototype.get=mu.prototype.get;mu.prototype.getKeys=mu.prototype.N;mu.prototype.getProperties=mu.prototype.O;mu.prototype.set=mu.prototype.set;mu.prototype.setProperties=mu.prototype.G;mu.prototype.unset=mu.prototype.P;mu.prototype.changed=mu.prototype.u;mu.prototype.dispatchEvent=mu.prototype.b;mu.prototype.getRevision=mu.prototype.K; +mu.prototype.on=mu.prototype.I;mu.prototype.once=mu.prototype.L;mu.prototype.un=mu.prototype.J;mu.prototype.unByKey=mu.prototype.M;qi.prototype.getActive=qi.prototype.f;qi.prototype.getMap=qi.prototype.l;qi.prototype.setActive=qi.prototype.i;qi.prototype.get=qi.prototype.get;qi.prototype.getKeys=qi.prototype.N;qi.prototype.getProperties=qi.prototype.O;qi.prototype.set=qi.prototype.set;qi.prototype.setProperties=qi.prototype.G;qi.prototype.unset=qi.prototype.P;qi.prototype.changed=qi.prototype.u; +qi.prototype.dispatchEvent=qi.prototype.b;qi.prototype.getRevision=qi.prototype.K;qi.prototype.on=qi.prototype.I;qi.prototype.once=qi.prototype.L;qi.prototype.un=qi.prototype.J;qi.prototype.unByKey=qi.prototype.M;Di.prototype.getGeometry=Di.prototype.W;Di.prototype.getActive=Di.prototype.f;Di.prototype.getMap=Di.prototype.l;Di.prototype.setActive=Di.prototype.i;Di.prototype.get=Di.prototype.get;Di.prototype.getKeys=Di.prototype.N;Di.prototype.getProperties=Di.prototype.O;Di.prototype.set=Di.prototype.set; +Di.prototype.setProperties=Di.prototype.G;Di.prototype.unset=Di.prototype.P;Di.prototype.changed=Di.prototype.u;Di.prototype.dispatchEvent=Di.prototype.b;Di.prototype.getRevision=Di.prototype.K;Di.prototype.on=Di.prototype.I;Di.prototype.once=Di.prototype.L;Di.prototype.un=Di.prototype.J;Di.prototype.unByKey=Di.prototype.M;qu.prototype.type=qu.prototype.type;qu.prototype.target=qu.prototype.target;qu.prototype.preventDefault=qu.prototype.preventDefault;qu.prototype.stopPropagation=qu.prototype.stopPropagation; +ru.prototype.getActive=ru.prototype.f;ru.prototype.getMap=ru.prototype.l;ru.prototype.setActive=ru.prototype.i;ru.prototype.get=ru.prototype.get;ru.prototype.getKeys=ru.prototype.N;ru.prototype.getProperties=ru.prototype.O;ru.prototype.set=ru.prototype.set;ru.prototype.setProperties=ru.prototype.G;ru.prototype.unset=ru.prototype.P;ru.prototype.changed=ru.prototype.u;ru.prototype.dispatchEvent=ru.prototype.b;ru.prototype.getRevision=ru.prototype.K;ru.prototype.on=ru.prototype.I;ru.prototype.once=ru.prototype.L; +ru.prototype.un=ru.prototype.J;ru.prototype.unByKey=ru.prototype.M;Ei.prototype.getActive=Ei.prototype.f;Ei.prototype.getMap=Ei.prototype.l;Ei.prototype.setActive=Ei.prototype.i;Ei.prototype.get=Ei.prototype.get;Ei.prototype.getKeys=Ei.prototype.N;Ei.prototype.getProperties=Ei.prototype.O;Ei.prototype.set=Ei.prototype.set;Ei.prototype.setProperties=Ei.prototype.G;Ei.prototype.unset=Ei.prototype.P;Ei.prototype.changed=Ei.prototype.u;Ei.prototype.dispatchEvent=Ei.prototype.b; +Ei.prototype.getRevision=Ei.prototype.K;Ei.prototype.on=Ei.prototype.I;Ei.prototype.once=Ei.prototype.L;Ei.prototype.un=Ei.prototype.J;Ei.prototype.unByKey=Ei.prototype.M;Gi.prototype.getActive=Gi.prototype.f;Gi.prototype.getMap=Gi.prototype.l;Gi.prototype.setActive=Gi.prototype.i;Gi.prototype.get=Gi.prototype.get;Gi.prototype.getKeys=Gi.prototype.N;Gi.prototype.getProperties=Gi.prototype.O;Gi.prototype.set=Gi.prototype.set;Gi.prototype.setProperties=Gi.prototype.G;Gi.prototype.unset=Gi.prototype.P; +Gi.prototype.changed=Gi.prototype.u;Gi.prototype.dispatchEvent=Gi.prototype.b;Gi.prototype.getRevision=Gi.prototype.K;Gi.prototype.on=Gi.prototype.I;Gi.prototype.once=Gi.prototype.L;Gi.prototype.un=Gi.prototype.J;Gi.prototype.unByKey=Gi.prototype.M;Hu.prototype.type=Hu.prototype.type;Hu.prototype.target=Hu.prototype.target;Hu.prototype.preventDefault=Hu.prototype.preventDefault;Hu.prototype.stopPropagation=Hu.prototype.stopPropagation;Iu.prototype.getActive=Iu.prototype.f;Iu.prototype.getMap=Iu.prototype.l; +Iu.prototype.setActive=Iu.prototype.i;Iu.prototype.get=Iu.prototype.get;Iu.prototype.getKeys=Iu.prototype.N;Iu.prototype.getProperties=Iu.prototype.O;Iu.prototype.set=Iu.prototype.set;Iu.prototype.setProperties=Iu.prototype.G;Iu.prototype.unset=Iu.prototype.P;Iu.prototype.changed=Iu.prototype.u;Iu.prototype.dispatchEvent=Iu.prototype.b;Iu.prototype.getRevision=Iu.prototype.K;Iu.prototype.on=Iu.prototype.I;Iu.prototype.once=Iu.prototype.L;Iu.prototype.un=Iu.prototype.J;Iu.prototype.unByKey=Iu.prototype.M; +Ii.prototype.getActive=Ii.prototype.f;Ii.prototype.getMap=Ii.prototype.l;Ii.prototype.setActive=Ii.prototype.i;Ii.prototype.get=Ii.prototype.get;Ii.prototype.getKeys=Ii.prototype.N;Ii.prototype.getProperties=Ii.prototype.O;Ii.prototype.set=Ii.prototype.set;Ii.prototype.setProperties=Ii.prototype.G;Ii.prototype.unset=Ii.prototype.P;Ii.prototype.changed=Ii.prototype.u;Ii.prototype.dispatchEvent=Ii.prototype.b;Ii.prototype.getRevision=Ii.prototype.K;Ii.prototype.on=Ii.prototype.I;Ii.prototype.once=Ii.prototype.L; +Ii.prototype.un=Ii.prototype.J;Ii.prototype.unByKey=Ii.prototype.M;Ki.prototype.getActive=Ki.prototype.f;Ki.prototype.getMap=Ki.prototype.l;Ki.prototype.setActive=Ki.prototype.i;Ki.prototype.get=Ki.prototype.get;Ki.prototype.getKeys=Ki.prototype.N;Ki.prototype.getProperties=Ki.prototype.O;Ki.prototype.set=Ki.prototype.set;Ki.prototype.setProperties=Ki.prototype.G;Ki.prototype.unset=Ki.prototype.P;Ki.prototype.changed=Ki.prototype.u;Ki.prototype.dispatchEvent=Ki.prototype.b; +Ki.prototype.getRevision=Ki.prototype.K;Ki.prototype.on=Ki.prototype.I;Ki.prototype.once=Ki.prototype.L;Ki.prototype.un=Ki.prototype.J;Ki.prototype.unByKey=Ki.prototype.M;Oi.prototype.getActive=Oi.prototype.f;Oi.prototype.getMap=Oi.prototype.l;Oi.prototype.setActive=Oi.prototype.i;Oi.prototype.get=Oi.prototype.get;Oi.prototype.getKeys=Oi.prototype.N;Oi.prototype.getProperties=Oi.prototype.O;Oi.prototype.set=Oi.prototype.set;Oi.prototype.setProperties=Oi.prototype.G;Oi.prototype.unset=Oi.prototype.P; +Oi.prototype.changed=Oi.prototype.u;Oi.prototype.dispatchEvent=Oi.prototype.b;Oi.prototype.getRevision=Oi.prototype.K;Oi.prototype.on=Oi.prototype.I;Oi.prototype.once=Oi.prototype.L;Oi.prototype.un=Oi.prototype.J;Oi.prototype.unByKey=Oi.prototype.M;Vu.prototype.type=Vu.prototype.type;Vu.prototype.target=Vu.prototype.target;Vu.prototype.preventDefault=Vu.prototype.preventDefault;Vu.prototype.stopPropagation=Vu.prototype.stopPropagation;Wu.prototype.getActive=Wu.prototype.f;Wu.prototype.getMap=Wu.prototype.l; +Wu.prototype.setActive=Wu.prototype.i;Wu.prototype.get=Wu.prototype.get;Wu.prototype.getKeys=Wu.prototype.N;Wu.prototype.getProperties=Wu.prototype.O;Wu.prototype.set=Wu.prototype.set;Wu.prototype.setProperties=Wu.prototype.G;Wu.prototype.unset=Wu.prototype.P;Wu.prototype.changed=Wu.prototype.u;Wu.prototype.dispatchEvent=Wu.prototype.b;Wu.prototype.getRevision=Wu.prototype.K;Wu.prototype.on=Wu.prototype.I;Wu.prototype.once=Wu.prototype.L;Wu.prototype.un=Wu.prototype.J;Wu.prototype.unByKey=Wu.prototype.M; +Zu.prototype.getActive=Zu.prototype.f;Zu.prototype.getMap=Zu.prototype.l;Zu.prototype.setActive=Zu.prototype.i;Zu.prototype.get=Zu.prototype.get;Zu.prototype.getKeys=Zu.prototype.N;Zu.prototype.getProperties=Zu.prototype.O;Zu.prototype.set=Zu.prototype.set;Zu.prototype.setProperties=Zu.prototype.G;Zu.prototype.unset=Zu.prototype.P;Zu.prototype.changed=Zu.prototype.u;Zu.prototype.dispatchEvent=Zu.prototype.b;Zu.prototype.getRevision=Zu.prototype.K;Zu.prototype.on=Zu.prototype.I;Zu.prototype.once=Zu.prototype.L; +Zu.prototype.un=Zu.prototype.J;Zu.prototype.unByKey=Zu.prototype.M;cv.prototype.type=cv.prototype.type;cv.prototype.target=cv.prototype.target;cv.prototype.preventDefault=cv.prototype.preventDefault;cv.prototype.stopPropagation=cv.prototype.stopPropagation;dv.prototype.getActive=dv.prototype.f;dv.prototype.getMap=dv.prototype.l;dv.prototype.setActive=dv.prototype.i;dv.prototype.get=dv.prototype.get;dv.prototype.getKeys=dv.prototype.N;dv.prototype.getProperties=dv.prototype.O;dv.prototype.set=dv.prototype.set; +dv.prototype.setProperties=dv.prototype.G;dv.prototype.unset=dv.prototype.P;dv.prototype.changed=dv.prototype.u;dv.prototype.dispatchEvent=dv.prototype.b;dv.prototype.getRevision=dv.prototype.K;dv.prototype.on=dv.prototype.I;dv.prototype.once=dv.prototype.L;dv.prototype.un=dv.prototype.J;dv.prototype.unByKey=dv.prototype.M;Tc.prototype.get=Tc.prototype.get;Tc.prototype.getKeys=Tc.prototype.N;Tc.prototype.getProperties=Tc.prototype.O;Tc.prototype.set=Tc.prototype.set;Tc.prototype.setProperties=Tc.prototype.G; +Tc.prototype.unset=Tc.prototype.P;Tc.prototype.changed=Tc.prototype.u;Tc.prototype.dispatchEvent=Tc.prototype.b;Tc.prototype.getRevision=Tc.prototype.K;Tc.prototype.on=Tc.prototype.I;Tc.prototype.once=Tc.prototype.L;Tc.prototype.un=Tc.prototype.J;Tc.prototype.unByKey=Tc.prototype.M;hd.prototype.getClosestPoint=hd.prototype.vb;hd.prototype.getExtent=hd.prototype.H;hd.prototype.rotate=hd.prototype.rotate;hd.prototype.simplify=hd.prototype.Bb;hd.prototype.transform=hd.prototype.jb;hd.prototype.get=hd.prototype.get; +hd.prototype.getKeys=hd.prototype.N;hd.prototype.getProperties=hd.prototype.O;hd.prototype.set=hd.prototype.set;hd.prototype.setProperties=hd.prototype.G;hd.prototype.unset=hd.prototype.P;hd.prototype.changed=hd.prototype.u;hd.prototype.dispatchEvent=hd.prototype.b;hd.prototype.getRevision=hd.prototype.K;hd.prototype.on=hd.prototype.I;hd.prototype.once=hd.prototype.L;hd.prototype.un=hd.prototype.J;hd.prototype.unByKey=hd.prototype.M;Vt.prototype.getFirstCoordinate=Vt.prototype.Ib; +Vt.prototype.getLastCoordinate=Vt.prototype.Jb;Vt.prototype.getLayout=Vt.prototype.Kb;Vt.prototype.rotate=Vt.prototype.rotate;Vt.prototype.getClosestPoint=Vt.prototype.vb;Vt.prototype.getExtent=Vt.prototype.H;Vt.prototype.simplify=Vt.prototype.Bb;Vt.prototype.get=Vt.prototype.get;Vt.prototype.getKeys=Vt.prototype.N;Vt.prototype.getProperties=Vt.prototype.O;Vt.prototype.set=Vt.prototype.set;Vt.prototype.setProperties=Vt.prototype.G;Vt.prototype.unset=Vt.prototype.P;Vt.prototype.changed=Vt.prototype.u; +Vt.prototype.dispatchEvent=Vt.prototype.b;Vt.prototype.getRevision=Vt.prototype.K;Vt.prototype.on=Vt.prototype.I;Vt.prototype.once=Vt.prototype.L;Vt.prototype.un=Vt.prototype.J;Vt.prototype.unByKey=Vt.prototype.M;Ln.prototype.getClosestPoint=Ln.prototype.vb;Ln.prototype.getExtent=Ln.prototype.H;Ln.prototype.rotate=Ln.prototype.rotate;Ln.prototype.simplify=Ln.prototype.Bb;Ln.prototype.transform=Ln.prototype.jb;Ln.prototype.get=Ln.prototype.get;Ln.prototype.getKeys=Ln.prototype.N; +Ln.prototype.getProperties=Ln.prototype.O;Ln.prototype.set=Ln.prototype.set;Ln.prototype.setProperties=Ln.prototype.G;Ln.prototype.unset=Ln.prototype.P;Ln.prototype.changed=Ln.prototype.u;Ln.prototype.dispatchEvent=Ln.prototype.b;Ln.prototype.getRevision=Ln.prototype.K;Ln.prototype.on=Ln.prototype.I;Ln.prototype.once=Ln.prototype.L;Ln.prototype.un=Ln.prototype.J;Ln.prototype.unByKey=Ln.prototype.M;zd.prototype.getFirstCoordinate=zd.prototype.Ib;zd.prototype.getLastCoordinate=zd.prototype.Jb; +zd.prototype.getLayout=zd.prototype.Kb;zd.prototype.rotate=zd.prototype.rotate;zd.prototype.getClosestPoint=zd.prototype.vb;zd.prototype.getExtent=zd.prototype.H;zd.prototype.simplify=zd.prototype.Bb;zd.prototype.transform=zd.prototype.jb;zd.prototype.get=zd.prototype.get;zd.prototype.getKeys=zd.prototype.N;zd.prototype.getProperties=zd.prototype.O;zd.prototype.set=zd.prototype.set;zd.prototype.setProperties=zd.prototype.G;zd.prototype.unset=zd.prototype.P;zd.prototype.changed=zd.prototype.u; +zd.prototype.dispatchEvent=zd.prototype.b;zd.prototype.getRevision=zd.prototype.K;zd.prototype.on=zd.prototype.I;zd.prototype.once=zd.prototype.L;zd.prototype.un=zd.prototype.J;zd.prototype.unByKey=zd.prototype.M;R.prototype.getFirstCoordinate=R.prototype.Ib;R.prototype.getLastCoordinate=R.prototype.Jb;R.prototype.getLayout=R.prototype.Kb;R.prototype.rotate=R.prototype.rotate;R.prototype.getClosestPoint=R.prototype.vb;R.prototype.getExtent=R.prototype.H;R.prototype.simplify=R.prototype.Bb; +R.prototype.transform=R.prototype.jb;R.prototype.get=R.prototype.get;R.prototype.getKeys=R.prototype.N;R.prototype.getProperties=R.prototype.O;R.prototype.set=R.prototype.set;R.prototype.setProperties=R.prototype.G;R.prototype.unset=R.prototype.P;R.prototype.changed=R.prototype.u;R.prototype.dispatchEvent=R.prototype.b;R.prototype.getRevision=R.prototype.K;R.prototype.on=R.prototype.I;R.prototype.once=R.prototype.L;R.prototype.un=R.prototype.J;R.prototype.unByKey=R.prototype.M; +S.prototype.getFirstCoordinate=S.prototype.Ib;S.prototype.getLastCoordinate=S.prototype.Jb;S.prototype.getLayout=S.prototype.Kb;S.prototype.rotate=S.prototype.rotate;S.prototype.getClosestPoint=S.prototype.vb;S.prototype.getExtent=S.prototype.H;S.prototype.simplify=S.prototype.Bb;S.prototype.transform=S.prototype.jb;S.prototype.get=S.prototype.get;S.prototype.getKeys=S.prototype.N;S.prototype.getProperties=S.prototype.O;S.prototype.set=S.prototype.set;S.prototype.setProperties=S.prototype.G; +S.prototype.unset=S.prototype.P;S.prototype.changed=S.prototype.u;S.prototype.dispatchEvent=S.prototype.b;S.prototype.getRevision=S.prototype.K;S.prototype.on=S.prototype.I;S.prototype.once=S.prototype.L;S.prototype.un=S.prototype.J;S.prototype.unByKey=S.prototype.M;Bn.prototype.getFirstCoordinate=Bn.prototype.Ib;Bn.prototype.getLastCoordinate=Bn.prototype.Jb;Bn.prototype.getLayout=Bn.prototype.Kb;Bn.prototype.rotate=Bn.prototype.rotate;Bn.prototype.getClosestPoint=Bn.prototype.vb; +Bn.prototype.getExtent=Bn.prototype.H;Bn.prototype.simplify=Bn.prototype.Bb;Bn.prototype.transform=Bn.prototype.jb;Bn.prototype.get=Bn.prototype.get;Bn.prototype.getKeys=Bn.prototype.N;Bn.prototype.getProperties=Bn.prototype.O;Bn.prototype.set=Bn.prototype.set;Bn.prototype.setProperties=Bn.prototype.G;Bn.prototype.unset=Bn.prototype.P;Bn.prototype.changed=Bn.prototype.u;Bn.prototype.dispatchEvent=Bn.prototype.b;Bn.prototype.getRevision=Bn.prototype.K;Bn.prototype.on=Bn.prototype.I; +Bn.prototype.once=Bn.prototype.L;Bn.prototype.un=Bn.prototype.J;Bn.prototype.unByKey=Bn.prototype.M;T.prototype.getFirstCoordinate=T.prototype.Ib;T.prototype.getLastCoordinate=T.prototype.Jb;T.prototype.getLayout=T.prototype.Kb;T.prototype.rotate=T.prototype.rotate;T.prototype.getClosestPoint=T.prototype.vb;T.prototype.getExtent=T.prototype.H;T.prototype.simplify=T.prototype.Bb;T.prototype.transform=T.prototype.jb;T.prototype.get=T.prototype.get;T.prototype.getKeys=T.prototype.N; +T.prototype.getProperties=T.prototype.O;T.prototype.set=T.prototype.set;T.prototype.setProperties=T.prototype.G;T.prototype.unset=T.prototype.P;T.prototype.changed=T.prototype.u;T.prototype.dispatchEvent=T.prototype.b;T.prototype.getRevision=T.prototype.K;T.prototype.on=T.prototype.I;T.prototype.once=T.prototype.L;T.prototype.un=T.prototype.J;T.prototype.unByKey=T.prototype.M;C.prototype.getFirstCoordinate=C.prototype.Ib;C.prototype.getLastCoordinate=C.prototype.Jb;C.prototype.getLayout=C.prototype.Kb; +C.prototype.rotate=C.prototype.rotate;C.prototype.getClosestPoint=C.prototype.vb;C.prototype.getExtent=C.prototype.H;C.prototype.simplify=C.prototype.Bb;C.prototype.transform=C.prototype.jb;C.prototype.get=C.prototype.get;C.prototype.getKeys=C.prototype.N;C.prototype.getProperties=C.prototype.O;C.prototype.set=C.prototype.set;C.prototype.setProperties=C.prototype.G;C.prototype.unset=C.prototype.P;C.prototype.changed=C.prototype.u;C.prototype.dispatchEvent=C.prototype.b;C.prototype.getRevision=C.prototype.K; +C.prototype.on=C.prototype.I;C.prototype.once=C.prototype.L;C.prototype.un=C.prototype.J;C.prototype.unByKey=C.prototype.M;E.prototype.getFirstCoordinate=E.prototype.Ib;E.prototype.getLastCoordinate=E.prototype.Jb;E.prototype.getLayout=E.prototype.Kb;E.prototype.rotate=E.prototype.rotate;E.prototype.getClosestPoint=E.prototype.vb;E.prototype.getExtent=E.prototype.H;E.prototype.simplify=E.prototype.Bb;E.prototype.transform=E.prototype.jb;E.prototype.get=E.prototype.get;E.prototype.getKeys=E.prototype.N; +E.prototype.getProperties=E.prototype.O;E.prototype.set=E.prototype.set;E.prototype.setProperties=E.prototype.G;E.prototype.unset=E.prototype.P;E.prototype.changed=E.prototype.u;E.prototype.dispatchEvent=E.prototype.b;E.prototype.getRevision=E.prototype.K;E.prototype.on=E.prototype.I;E.prototype.once=E.prototype.L;E.prototype.un=E.prototype.J;E.prototype.unByKey=E.prototype.M;ko.prototype.readFeatures=ko.prototype.Fa;lo.prototype.readFeatures=lo.prototype.Fa;lo.prototype.readFeatures=lo.prototype.Fa; +Xe.prototype.get=Xe.prototype.get;Xe.prototype.getKeys=Xe.prototype.N;Xe.prototype.getProperties=Xe.prototype.O;Xe.prototype.set=Xe.prototype.set;Xe.prototype.setProperties=Xe.prototype.G;Xe.prototype.unset=Xe.prototype.P;Xe.prototype.changed=Xe.prototype.u;Xe.prototype.dispatchEvent=Xe.prototype.b;Xe.prototype.getRevision=Xe.prototype.K;Xe.prototype.on=Xe.prototype.I;Xe.prototype.once=Xe.prototype.L;Xe.prototype.un=Xe.prototype.J;Xe.prototype.unByKey=Xe.prototype.M;Ef.prototype.getMap=Ef.prototype.i; +Ef.prototype.setMap=Ef.prototype.setMap;Ef.prototype.setTarget=Ef.prototype.c;Ef.prototype.get=Ef.prototype.get;Ef.prototype.getKeys=Ef.prototype.N;Ef.prototype.getProperties=Ef.prototype.O;Ef.prototype.set=Ef.prototype.set;Ef.prototype.setProperties=Ef.prototype.G;Ef.prototype.unset=Ef.prototype.P;Ef.prototype.changed=Ef.prototype.u;Ef.prototype.dispatchEvent=Ef.prototype.b;Ef.prototype.getRevision=Ef.prototype.K;Ef.prototype.on=Ef.prototype.I;Ef.prototype.once=Ef.prototype.L;Ef.prototype.un=Ef.prototype.J; +Ef.prototype.unByKey=Ef.prototype.M;Lf.prototype.getMap=Lf.prototype.i;Lf.prototype.setMap=Lf.prototype.setMap;Lf.prototype.setTarget=Lf.prototype.c;Lf.prototype.get=Lf.prototype.get;Lf.prototype.getKeys=Lf.prototype.N;Lf.prototype.getProperties=Lf.prototype.O;Lf.prototype.set=Lf.prototype.set;Lf.prototype.setProperties=Lf.prototype.G;Lf.prototype.unset=Lf.prototype.P;Lf.prototype.changed=Lf.prototype.u;Lf.prototype.dispatchEvent=Lf.prototype.b;Lf.prototype.getRevision=Lf.prototype.K; +Lf.prototype.on=Lf.prototype.I;Lf.prototype.once=Lf.prototype.L;Lf.prototype.un=Lf.prototype.J;Lf.prototype.unByKey=Lf.prototype.M;Qf.prototype.getMap=Qf.prototype.i;Qf.prototype.setMap=Qf.prototype.setMap;Qf.prototype.setTarget=Qf.prototype.c;Qf.prototype.get=Qf.prototype.get;Qf.prototype.getKeys=Qf.prototype.N;Qf.prototype.getProperties=Qf.prototype.O;Qf.prototype.set=Qf.prototype.set;Qf.prototype.setProperties=Qf.prototype.G;Qf.prototype.unset=Qf.prototype.P;Qf.prototype.changed=Qf.prototype.u; +Qf.prototype.dispatchEvent=Qf.prototype.b;Qf.prototype.getRevision=Qf.prototype.K;Qf.prototype.on=Qf.prototype.I;Qf.prototype.once=Qf.prototype.L;Qf.prototype.un=Qf.prototype.J;Qf.prototype.unByKey=Qf.prototype.M;an.prototype.getMap=an.prototype.i;an.prototype.setMap=an.prototype.setMap;an.prototype.setTarget=an.prototype.c;an.prototype.get=an.prototype.get;an.prototype.getKeys=an.prototype.N;an.prototype.getProperties=an.prototype.O;an.prototype.set=an.prototype.set;an.prototype.setProperties=an.prototype.G; +an.prototype.unset=an.prototype.P;an.prototype.changed=an.prototype.u;an.prototype.dispatchEvent=an.prototype.b;an.prototype.getRevision=an.prototype.K;an.prototype.on=an.prototype.I;an.prototype.once=an.prototype.L;an.prototype.un=an.prototype.J;an.prototype.unByKey=an.prototype.M;Hf.prototype.getMap=Hf.prototype.i;Hf.prototype.setMap=Hf.prototype.setMap;Hf.prototype.setTarget=Hf.prototype.c;Hf.prototype.get=Hf.prototype.get;Hf.prototype.getKeys=Hf.prototype.N;Hf.prototype.getProperties=Hf.prototype.O; +Hf.prototype.set=Hf.prototype.set;Hf.prototype.setProperties=Hf.prototype.G;Hf.prototype.unset=Hf.prototype.P;Hf.prototype.changed=Hf.prototype.u;Hf.prototype.dispatchEvent=Hf.prototype.b;Hf.prototype.getRevision=Hf.prototype.K;Hf.prototype.on=Hf.prototype.I;Hf.prototype.once=Hf.prototype.L;Hf.prototype.un=Hf.prototype.J;Hf.prototype.unByKey=Hf.prototype.M;fn.prototype.getMap=fn.prototype.i;fn.prototype.setMap=fn.prototype.setMap;fn.prototype.setTarget=fn.prototype.c;fn.prototype.get=fn.prototype.get; +fn.prototype.getKeys=fn.prototype.N;fn.prototype.getProperties=fn.prototype.O;fn.prototype.set=fn.prototype.set;fn.prototype.setProperties=fn.prototype.G;fn.prototype.unset=fn.prototype.P;fn.prototype.changed=fn.prototype.u;fn.prototype.dispatchEvent=fn.prototype.b;fn.prototype.getRevision=fn.prototype.K;fn.prototype.on=fn.prototype.I;fn.prototype.once=fn.prototype.L;fn.prototype.un=fn.prototype.J;fn.prototype.unByKey=fn.prototype.M;Jf.prototype.getMap=Jf.prototype.i;Jf.prototype.setMap=Jf.prototype.setMap; +Jf.prototype.setTarget=Jf.prototype.c;Jf.prototype.get=Jf.prototype.get;Jf.prototype.getKeys=Jf.prototype.N;Jf.prototype.getProperties=Jf.prototype.O;Jf.prototype.set=Jf.prototype.set;Jf.prototype.setProperties=Jf.prototype.G;Jf.prototype.unset=Jf.prototype.P;Jf.prototype.changed=Jf.prototype.u;Jf.prototype.dispatchEvent=Jf.prototype.b;Jf.prototype.getRevision=Jf.prototype.K;Jf.prototype.on=Jf.prototype.I;Jf.prototype.once=Jf.prototype.L;Jf.prototype.un=Jf.prototype.J;Jf.prototype.unByKey=Jf.prototype.M; +kn.prototype.getMap=kn.prototype.i;kn.prototype.setMap=kn.prototype.setMap;kn.prototype.setTarget=kn.prototype.c;kn.prototype.get=kn.prototype.get;kn.prototype.getKeys=kn.prototype.N;kn.prototype.getProperties=kn.prototype.O;kn.prototype.set=kn.prototype.set;kn.prototype.setProperties=kn.prototype.G;kn.prototype.unset=kn.prototype.P;kn.prototype.changed=kn.prototype.u;kn.prototype.dispatchEvent=kn.prototype.b;kn.prototype.getRevision=kn.prototype.K;kn.prototype.on=kn.prototype.I; +kn.prototype.once=kn.prototype.L;kn.prototype.un=kn.prototype.J;kn.prototype.unByKey=kn.prototype.M;pn.prototype.getMap=pn.prototype.i;pn.prototype.setMap=pn.prototype.setMap;pn.prototype.setTarget=pn.prototype.c;pn.prototype.get=pn.prototype.get;pn.prototype.getKeys=pn.prototype.N;pn.prototype.getProperties=pn.prototype.O;pn.prototype.set=pn.prototype.set;pn.prototype.setProperties=pn.prototype.G;pn.prototype.unset=pn.prototype.P;pn.prototype.changed=pn.prototype.u;pn.prototype.dispatchEvent=pn.prototype.b; +pn.prototype.getRevision=pn.prototype.K;pn.prototype.on=pn.prototype.I;pn.prototype.once=pn.prototype.L;pn.prototype.un=pn.prototype.J;pn.prototype.unByKey=pn.prototype.M; + return OPENLAYERS.ol; +})); + diff --git a/themes/bootstrap3/less/components/search.less b/themes/bootstrap3/less/components/search.less index 44c62658b76e9aabe86b8ab529ddccebd1bdfab3..53b7ccc70c36f28bc741a747946543f3b566469d 100644 --- a/themes/bootstrap3/less/components/search.less +++ b/themes/bootstrap3/less/components/search.less @@ -119,3 +119,8 @@ } .wikipedia img { margin-right: 1rem; } + +.geoItem { + font-size: .9em; + margin: 0px 0px 10px; +} diff --git a/themes/bootstrap3/templates/Recommend/MapSelection.phtml b/themes/bootstrap3/templates/Recommend/MapSelection.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4170ec5e442b20bcddcb7c2f43dbae0a14c4787b --- /dev/null +++ b/themes/bootstrap3/templates/Recommend/MapSelection.phtml @@ -0,0 +1,43 @@ +<? if ($this->recommend->getSearchResultCoordinates()) :?> +<? + $this->headScript()->appendFile('vendor/ol/ol.js'); + $this->headScript()->appendFile('map_selection.js'); + $this->headLink()->appendStylesheet('vendor/ol/ol.css'); + + $resultsCoords = $this->recommend->getMapResultCoordinates(); + $baseUrl = $this->url('home'); + $geoField = $this->recommend->getGeoField(); + $urlpath = $this->url('search-results'); + $coordinates = $this->recommend->getSelectedCoordinates(); + $showSelection = true; + $popupTitle = $this->transEsc('map_results_label'); + if ($coordinates == null) { + $coordinates = $this->recommend->getDefaultCoordinates(); + $showSelection = false; + } + $height = $this->recommend->getHeight(); + $searchParams = $this->recommend->getSearchParams(); + $params = array(json_encode($geoField), json_encode($coordinates), json_encode($urlpath), + json_encode($baseUrl), json_encode($searchParams), json_encode($showSelection), + json_encode($resultsCoords), json_encode($popupTitle)); + $jsParams = implode(', ', $params); + $jsLoad = "loadMapSelection(" . $jsParams . ");"; + $addSearchOption = <<<EOF + $('.search-query-options>.advanced').after(' <a href="#" class="advanced" onclick=\\'{$jsLoad} $(this).remove(); return false;\\'>{$this->transEsc('Geographic Search')}</a>'); +EOF; +?> +<div class="authorbox"> + <div id="geo_search" style="display: none;"> + <button id="draw_box"><?=$this->transEsc('Draw Search Box')?></button> + <span class="geo_maphelp"> <a href="<?=$this->url('help-home')?>?topic=geosearch" data-lightbox class="help-link"><?=$this->transEsc('Need Help?')?></a></span> + <div id="geo_search_map" style="height: <?=$height?>px;"> + <div id="popup"></div> + </div> + </div> + <? if ($showSelection) :?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $jsLoad, 'SET');?> + <? else: ?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $addSearchOption, 'SET');?> + <? endif; ?> +</div> +<? endif; ?> diff --git a/themes/bootstrap3/templates/RecordTab/map.phtml b/themes/bootstrap3/templates/RecordTab/map.phtml index b86147f61b3e98efa4ee7c17d4347f8c8ae98572..462bcbbfe9056ba87975088eeea2a8da8dbcdce0 100644 --- a/themes/bootstrap3/templates/RecordTab/map.phtml +++ b/themes/bootstrap3/templates/RecordTab/map.phtml @@ -1,13 +1,27 @@ -<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<?=$this->tab->getGoogleMapApiKey()?>&language=<?=$this->layout()->userLang?>"></script> - -<script type="text/javascript"> - -var markers; -var markersData; -var latlng; -var myOptions; -var map; -var infowindow = new google.maps.InfoWindow({maxWidth: 480, minWidth: 480}); +<? $mapType = $this->tab->getMapType(); ?> +<? if (isset($mapType) && ($mapType == 'openlayers')) : ?> + <? $this->headScript()->appendFile('vendor/ol/ol.js'); + $this->headScript()->appendFile('map_tab_ol.js'); + $this->headLink()->appendStylesheet('vendor/ol/ol.css'); + $mapTabData = $this->tab->getMapTabData(); + $popupTitle = $this->transEsc('map_results_label'); + $params = array(json_encode($mapTabData), json_encode($popupTitle)); + $jsParams = implode(', ', $params); + $jsLoad = "loadMapTab(" . $jsParams . ");"; ?> + <div id="wrap" style="width: 674px; height: 479px"> + <div id="map-canvas" style="width: 100%; height: 100%"><div id="popup"></div></div> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $jsLoad, 'SET');?> + </div> +<? endif; ?> +<? if (isset($mapType) && ($mapType == 'google')) : ?> + <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=<?=$this->tab->getGoogleMapApiKey()?>&language=<?=$this->layout()->userLang?>"></script> + <script type="text/javascript"> + var markers; + var markersData; + var latlng; + var myOptions; + var map; + var infowindow = new google.maps.InfoWindow({maxWidth: 480, minWidth: 480}); function initialize() { markersData = <?=$this->tab->getGoogleMapMarker()?>; latlng = new google.maps.LatLng(0, 0); @@ -27,16 +41,15 @@ var infowindow = new google.maps.InfoWindow({maxWidth: 480, minWidth: 480}); function showMarkers(){ deleteOverlays(); markers = []; - for (var i = 0; i<markersData.length; i++){ - var disTitle = markersData[i].title; + var disTitle = markersData[i][0].title ? markersData[i][0].title : ''; var iconTitle = disTitle; if (disTitle.length>25){ iconTitle = disTitle.substring(0,25) + "..."; } var markerImg = "https://chart.googleapis.com/chart?chst=d_bubble_text_small&chld=edge_bc|" + iconTitle +"|EEEAE3|"; var labelXoffset = 1 + disTitle.length * 4; - var latLng = new google.maps.LatLng(markersData[i].lat , markersData[i].lon) + var latLng = new google.maps.LatLng(markersData[i][0].lat , markersData[i][0].lon) var marker = new google.maps.Marker({ position: latLng, map: map, @@ -63,3 +76,4 @@ var infowindow = new google.maps.InfoWindow({maxWidth: 480, minWidth: 480}); <div id="wrap" onload="initialize()" style="width: 674px; height: 479px"> <div id="map_canvas" style="width: 100%; height: 100%"></div> </div> +<? endif; ?> diff --git a/themes/bootstrap3/templates/search/searchbox.phtml b/themes/bootstrap3/templates/search/searchbox.phtml index cae6aa5442136d367482e0d04e8eead1695735f9..e7d0c4ea19db9434d8d1cc4d473d032ece871cdb 100644 --- a/themes/bootstrap3/templates/search/searchbox.phtml +++ b/themes/bootstrap3/templates/search/searchbox.phtml @@ -56,6 +56,9 @@ <? if ($advSearch): ?> <a href="<?=$this->url($advSearch) . ((isset($this->searchId) && $this->searchId) ? '?edit=' . $this->escapeHtmlAttr($this->searchId) : $hiddenFilterParams) ?>" class="btn btn-link" rel="nofollow"><?=$this->transEsc("Advanced")?></a> <? endif; ?> + <? if ($geoUrl = $this->geocoords()->getSearchUrl($options)) : ?> + <a href="<?=$geoUrl ?>" class="btn btn-link"><?=$this->transEsc('Geographic Search')?></a> + <? endif; ?> <? $shards = $options->getShards(); if ($options->showShardCheckboxes() && !empty($shards)): ?> <? diff --git a/themes/root/templates/HelpTranslations/en/geosearch.phtml b/themes/root/templates/HelpTranslations/en/geosearch.phtml new file mode 100644 index 0000000000000000000000000000000000000000..4872934a1d2ecedc46b6ad61035254de73c68e90 --- /dev/null +++ b/themes/root/templates/HelpTranslations/en/geosearch.phtml @@ -0,0 +1,23 @@ +<h1>Geographic Search Tips</h1> +<dl class="Content"> +<dt></dt> + <dd> + <h3>To draw the search box:</h3> + <ol> + <li>Click on the 'Draw Search Box' button.</li> + <li>Click and release on starting point of the search box.</li> + <li>Drag the search box to the cover the area of the map you wish to search.</li> + <li>Click and release on the ending point of the search box.</li> + </ol> + <h3>Interacting with the search results</h3> + <p>Search results are displayed on the map as clusters (circular icons with numbers). The numbers represent + the number of records in each cluster. A list of results is displayed below the map view. </p> + <p>You can interact with the map by zooming by either using the zoom buttons (+/-) or by using the mouse to + double-click (zoom in) or shift + double-click (zoom out).</p> + <p>Clusters with less than 5 records will display pop-up information about the records at that location. + When you hover over a cluster with less than 5 records, the mouse pointer will change to a hand with a finger + pointing at the cluster. Click on the cluster, and a pop-up window will appear showing a list of titles for the + records at that location. Each title is hyperlinked to the associated record. + Click on the title to view the full record.</p> + </dd> +</dl> diff --git a/themes/root/theme.config.php b/themes/root/theme.config.php index 7822da54141bb0154d9288f456d48cde82ec21ea..0de80f7ed5c50f93f94d52b7fd234a447944be53 100644 --- a/themes/root/theme.config.php +++ b/themes/root/theme.config.php @@ -15,6 +15,7 @@ return array( 'export' => 'VuFind\View\Helper\Root\Factory::getExport', 'feedback' => 'VuFind\View\Helper\Root\Factory::getFeedback', 'flashmessages' => 'VuFind\View\Helper\Root\Factory::getFlashmessages', + 'geocoords' => 'VuFind\View\Helper\Root\Factory::getGeoCoords', 'googleanalytics' => 'VuFind\View\Helper\Root\Factory::getGoogleAnalytics', 'helptext' => 'VuFind\View\Helper\Root\Factory::getHelpText', 'historylabel' => 'VuFind\View\Helper\Root\Factory::getHistoryLabel',