diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index f4e119016..af2be862e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -9,10 +9,6 @@ ISSUES ARE NOT FOR SUPPORT!!! ISSUES ARE NOT FOR SUPPORT!!! - Join discord (channel: #help) for support at this link: https://discord.gg/rocketmap - - Submit feature requests @ http://vote.devkat.org - --> diff --git a/Gruntfile.js b/Gruntfile.js index 4984b706b..21c86f427 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -45,10 +45,9 @@ module.exports = function (grunt) { 'static/dist/js/statistics.built.js': 'static/js/statistics.js', 'static/dist/js/status.built.js': 'static/js/status.js', 'static/dist/js/custom.built.js': 'static/js/custom.js', - 'static/dist/js/vendor/markerclusterer.built.js': 'static/js/vendor/markerclusterer.js', 'static/dist/js/serviceWorker.built.js': 'static/js/serviceWorker.js', 'static/dist/js/weather.built.js': 'static/js/weather.js' - } + } } }, uglify: { @@ -69,7 +68,6 @@ module.exports = function (grunt) { 'static/dist/js/statistics.min.js': 'static/dist/js/statistics.built.js', 'static/dist/js/status.min.js': 'static/dist/js/status.built.js', 'static/dist/js/custom.min.js': 'static/dist/js/custom.built.js', - 'static/dist/js/vendor/markerclusterer.min.js': 'static/dist/js/vendor/markerclusterer.built.js', 'static/dist/js/serviceWorker.min.js': 'static/dist/js/serviceWorker.built.js', 'static/dist/js/weather.min.js': 'static/dist/js/weather.built.js' } diff --git a/README.md b/README.md index 8718ed0fc..a3fd10a29 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,7 @@ Live visualization of all the Pokémon (with option to show gyms, raids and Pok * Incredibly fast, efficient searching algorithm (compared to everything else available) ## Information -* [Discord](https://discord.gg/rocketmap) for general support -* [Documentation](https://rocketmap.readthedocs.io/) for installation and usage docs -* [vote.devkat.org](http://vote.devkat.org) to request new features -* [Github Issues](https://github.com/RocketMap/RocketMap/issues) for reporting bugs (not for support!) +* [Github Issues](https://github.com/SenorKarlos/RocketMap/issues) for reporting bugs (not for support!) ## Installation diff --git a/pogom/altitude.py b/pogom/altitude.py index 180c535e2..4d04fd1ef 100644 --- a/pogom/altitude.py +++ b/pogom/altitude.py @@ -12,20 +12,20 @@ fallback_altitude = None -def get_gmaps_altitude(lat, lng, gmaps_key): +def get_gmaps_altitude(lat, lng): try: r_session = requests.Session() response = r_session.get(( - 'https://maps.googleapis.com/maps/api/elevation/json?' + - 'locations={},{}&key={}').format(lat, lng, gmaps_key), - timeout=5) + 'https://api.open-elevation.com/api/v1/lookup?' + + 'locations={},{}').format(lat, lng), + timeout=30) response = response.json() - status = response['status'] + status = response['results'] results = response.get('results', []) result = results[0] if results else {} altitude = result.get('elevation', None) except Exception as e: - log.exception('Unable to retrieve altitude from Google APIs: %s.', e) + log.exception('Unable to retrieve altitude from OSM APIs: %s.', e) status = 'UNKNOWN_ERROR' altitude = None @@ -50,8 +50,7 @@ def get_fallback_altitude(args, loc): # Only query if it's not set, and if it didn't fail already. if fallback_altitude is None and fallback_altitude != -1: - (fallback_altitude, status) = get_gmaps_altitude(loc[0], loc[1], - args.gmaps_key) + (fallback_altitude, status) = get_gmaps_altitude(loc[0], loc[1]) # Failed, don't try again. if fallback_altitude is None: @@ -66,7 +65,7 @@ def cached_get_altitude(args, loc): altitude = LocationAltitude.get_nearby_altitude(loc) if altitude is None: - (altitude, status) = get_gmaps_altitude(loc[0], loc[1], args.gmaps_key) + (altitude, status) = get_gmaps_altitude(loc[0], loc[1]) if altitude is not None and altitude != -1: LocationAltitude.save_altitude(loc, altitude) diff --git a/pogom/app.py b/pogom/app.py index 556723c0c..73872b73e 100644 --- a/pogom/app.py +++ b/pogom/app.py @@ -374,7 +374,6 @@ def fullmap(self, statusname=None): lng=map_lng, showAllZoomLevel=args.show_all_zoom_level, generateImages=str(args.generate_images).lower(), - gmaps_key=args.gmaps_key, lang=args.locale, show=visibility_flags, rarityFileName=args.rarity_filename) @@ -701,7 +700,6 @@ def get_stats(self): lat=self.current_location[0], lng=self.current_location[1], generateImages=str(args.generate_images).lower(), - gmaps_key=args.gmaps_key, show=visibility_flags) def get_gymdata(self): diff --git a/pogom/dyn_img.py b/pogom/dyn_img.py index 6062819d4..3073aaf31 100644 --- a/pogom/dyn_img.py +++ b/pogom/dyn_img.py @@ -187,7 +187,6 @@ def get_gym_icon(team, level, raidlevel, pkm, is_in_battle): if is_in_battle: out_filename = out_filename.replace('.png', '_B.png') im_lines.extend(draw_battle_indicator()) - gym_image = os.path.join(path_gym, '{}.png'.format(team)) return run_imagemagick(gym_image, im_lines, out_filename) @@ -326,9 +325,9 @@ def pokemon_asset_path(pkm, classifier=None, gender=GENDER_UNSET, log.warning("Cannot find PogoAssets file {}".format( assets_fullname)) # Dummy Pokemon icon - return os.path.join( - assets_basedir, 'pokemon_icon_000.png'), - os.path.join(target_path, 'pkm_000.png') + return ( + os.path.join(assets_basedir, 'pokemon_icon_000.png'), + os.path.join(target_path, 'pkm_000.png')) return pokemon_asset_path(pkm, classifier=classifier, gender=MALE, form=form, costume=costume, weather=weather, @@ -338,10 +337,10 @@ def pokemon_asset_path(pkm, classifier=None, gender=GENDER_UNSET, def draw_gym_subject(image, size, gravity='north', trim=False): trim_cmd = ' -fuzz 0.5% -trim +repage' if trim else '' lines = [ - '-gravity {} ( "{}"{} -scale {}x{} -unsharp 0x1 ( +clone ' + + '-gravity {} ( "{}"{} -scale {}x{} -unsharp 0x1 ( +clone '.format( + gravity, image, trim_cmd, size, size), '-background black -shadow 80x3+5+5 ) +swap -background ' + - 'none -layers merge +repage ) -geometry +0+0 -composite'.format( - gravity, image, trim_cmd, size, size) + 'none -layers merge +repage ) -geometry +0+0 -composite' ] return lines @@ -351,9 +350,10 @@ def draw_badge(pos, fill_col, text_col, text): lines = [ '-fill {} -stroke black -draw "circle {},{} {},{}"'.format( fill_col, x, y, x + gym_badge_radius, y), - '-gravity center -fill {} -stroke none ' + + '-gravity center -fill {} -stroke none '.format( + text_col), '-draw "text {},{} \'{}\'"'.format( - text_col, x - 48, y - 49, text) + x - 48, y - 49, text) ] return lines diff --git a/pogom/pgscout.py b/pogom/pgscout.py index b63b21480..b49f9382c 100644 --- a/pogom/pgscout.py +++ b/pogom/pgscout.py @@ -1,5 +1,4 @@ import logging -import sys import requests @@ -34,7 +33,7 @@ def pgscout_encounter(p, forced=False): r = requests.get(args.pgscout_url, params=params) except Exception: return scout_error( - "Exception on scout: {}".format(repr(sys.exc_info()[1]))) + "Exception on scout") return r.json() if r.status_code == 200 else scout_error( "Got error {} from scout service.".format(r.status_code)) @@ -52,7 +51,7 @@ def perform_lure(p): r = requests.get(args.lure_url, params=params) except Exception: return scout_error( - "Exception on request: {}".format(repr(sys.exc_info()[1]))) + "Exception on request") return r.json() if r.status_code == 200 else scout_error( "Got error {} from service.".format(r.status_code)) diff --git a/pogom/utils.py b/pogom/utils.py index e00f14494..11c666b00 100644 --- a/pogom/utils.py +++ b/pogom/utils.py @@ -17,7 +17,7 @@ import requests from s2sphere import CellId, LatLng -from geopy.geocoders import GoogleV3 +from geopy.geocoders import Nominatim from requests_futures.sessions import FuturesSession from requests.packages.urllib3.util.retry import Retry from requests.adapters import HTTPAdapter @@ -288,9 +288,6 @@ def get_args(): 'search bar for use in shared maps.', action='store_false', dest='fixed_location', default=True) - parser.add_argument('-k', '--gmaps-key', - help='Google Maps Javascript API Key.', - required=True) parser.add_argument('--skip-empty', help=('Enables skipping of empty cells in normal ' + 'scans - requires previously populated ' + @@ -1183,10 +1180,23 @@ def calc_pokemon_level(cp_multiplier): return pokemon_level +def get_pos_by_name(location_name): + geolocator = Nominatim() + loc = geolocator.geocode(location_name, timeout=10) + if not loc: + return None + + log.info("Location for '%s' found: %s", location_name, loc.address) + log.info('Coordinates (lat/long/alt) for location: %s %s %s', loc.latitude, + loc.longitude, loc.altitude) + + return (loc.latitude, loc.longitude, loc.altitude) + + @memoize -def gmaps_reverse_geolocate(gmaps_key, locale, location): +def gmaps_reverse_geolocate(locale, location): # Find the reverse geolocation - geolocator = GoogleV3(api_key=gmaps_key) + geolocator = Nominatim() player_locale = { 'country': 'US', @@ -1216,11 +1226,8 @@ def gmaps_reverse_geolocate(gmaps_key, locale, location): 'timezone': str(timezone) }) except Exception as e: - log.exception('Exception on Google Timezone API. ' - + 'Please check that you have Google Timezone API' - + ' enabled for your API key' - + ' (https://developers.google.com/maps/' - + 'documentation/timezone/intro): %s.', e) + log.exception('Exception on Timezone API. ' + + 'Please check that you have Timezone API', e) except Exception as e: log.exception('Exception while obtaining player locale: %s.' + ' Using default locale.', e) diff --git a/runserver.py b/runserver.py index 3e0605eb1..3570739fa 100755 --- a/runserver.py +++ b/runserver.py @@ -23,7 +23,7 @@ from pogom.app import Pogom from pogom.utils import (get_args, now, gmaps_reverse_geolocate, init_args, log_resource_usage_loop, get_debug_dump_link, - dynamic_rarity_refresher) + dynamic_rarity_refresher, get_pos_by_name) from pogom.altitude import get_gmaps_altitude from pogom.models import (init_database, create_tables, drop_tables, @@ -89,7 +89,7 @@ def filter(self, record): # Assert pgoapi is installed. try: import pgoapi - from pgoapi import PGoApi, utilities as util + from pgoapi import PGoApi except ImportError: log.critical( "It seems `pgoapi` is not installed. Try running " + @@ -262,7 +262,7 @@ def extract_coordinates(location): position = (float(res.group(1)), float(res.group(2)), 0) else: log.debug('Looking up coordinates in API') - position = util.get_pos_by_name(location) + position = get_pos_by_name(location) if position is None or not any(position): log.error("Location not found: '{}'".format(location)) @@ -334,20 +334,18 @@ def main(): position = extract_coordinates(args.location) # Use the latitude and longitude to get the local altitude from Google. - (altitude, status) = get_gmaps_altitude(position[0], position[1], - args.gmaps_key) + (altitude, results) = get_gmaps_altitude(position[0], position[1]) + if altitude is not None: log.debug('Local altitude is: %sm.', altitude) position = (position[0], position[1], altitude) else: - if status == 'REQUEST_DENIED': + if results == 'REQUEST_DENIED': log.error( - 'Google API Elevation request was denied. You probably ' + - 'forgot to enable elevation api in https://console.' + - 'developers.google.com/apis/api/elevation_backend/') + 'OSM API Elevation request was denied.') sys.exit() else: - log.error('Unable to retrieve altitude from Google APIs' + + log.error('Unable to retrieve altitude from OSM APIs' + 'setting to 0') log.info('Parsed location is: %.4f/%.4f/%.4f (lat/lng/alt).', @@ -452,7 +450,6 @@ def main(): args.player_locale = PlayerLocale.get_locale(args.location) if not args.player_locale: args.player_locale = gmaps_reverse_geolocate( - args.gmaps_key, args.locale, str(position[0]) + ', ' + str(position[1])) db_player_locale = { diff --git a/static/data/mapstyle.json b/static/data/mapstyle.json index c8f221ba7..f2e3c83bc 100644 --- a/static/data/mapstyle.json +++ b/static/data/mapstyle.json @@ -1,15 +1,7 @@ { - "roadmap": "Roadmap", - "satellite": "Satellite", - "hybrid": "Hybrid", - "nolabels_style": "No Labels", - "dark_style": "Dark", - "style_light2": "Light2", - "style_pgo": "Pokémon Go", - "dark_style_nl": "Dark (No Labels)", - "style_light2_nl": "Light2 (No Labels)", - "style_pgo_nl": "Pokémon Go (No Labels)", - "style_pgo_day": "Pokémon Go Day", - "style_pgo_night": "Pokémon Go Night", - "style_pgo_dynamic": "Pokémon Go Dynamic" + "stylemapnik": "Mapnik", + "styleblackandwhite": "Black and White", + "styletopo": "ToPo Map", + "stylesatellite": "Satellite", + "stylewikipedia": "Wikipedia" } diff --git a/static/data/searchmarkerstyle.json b/static/data/searchmarkerstyle.json index 58c7a0cdf..ba5ecf3c1 100644 --- a/static/data/searchmarkerstyle.json +++ b/static/data/searchmarkerstyle.json @@ -11,8 +11,8 @@ "name": "Compass", "icon": "static/images/markers/compass.png" }, - "google": { - "name": "Default Google Marker", + "osm": { + "name": "Default OSM Marker", "icon": null }, "female": { diff --git a/static/images/cluster/m1.png b/static/images/cluster/m1.png deleted file mode 100644 index 329ff524c..000000000 Binary files a/static/images/cluster/m1.png and /dev/null differ diff --git a/static/images/cluster/m2.png b/static/images/cluster/m2.png deleted file mode 100644 index b999cbcf6..000000000 Binary files a/static/images/cluster/m2.png and /dev/null differ diff --git a/static/images/cluster/m3.png b/static/images/cluster/m3.png deleted file mode 100644 index 9f30b3092..000000000 Binary files a/static/images/cluster/m3.png and /dev/null differ diff --git a/static/images/cluster/m4.png b/static/images/cluster/m4.png deleted file mode 100644 index 0d3f8263b..000000000 Binary files a/static/images/cluster/m4.png and /dev/null differ diff --git a/static/images/cluster/m5.png b/static/images/cluster/m5.png deleted file mode 100644 index 61387d2ab..000000000 Binary files a/static/images/cluster/m5.png and /dev/null differ diff --git a/static/js/custom.js.example b/static/js/custom.js.example index cfdf9a0e1..e0ed4d418 100644 --- a/static/js/custom.js.example +++ b/static/js/custom.js.example @@ -2,7 +2,7 @@ $(function () { 'use strict' /* Settings. */ - const showSearchMarker = true // Show a marker on the map's scan location. Default: false. + const showSearchMarker = true // Show a marker on the map's scan location. Default: false. const isSearchMarkerMovable = false // Let the user move the scan location marker around. Doesn't do anything without --no-fixed-location. Default: false. const showLocationMarker = true // Show a marker on the visitor's location. Default: false. const isLocationMarkerMovable = false // Let the user move the visitor marker around. Default: false. @@ -15,6 +15,126 @@ $(function () { // Looks like 'UA-XXXXX-Y'. const analyticsKey = '' + // Hide Presets + + const hidepresets= [ + { + PokemonID: 149, + Name: "only Dragon", + Searchstring: ['Dragon'], + Invert: true + }, + { + PokemonID: 230, + Name: "only Water", + Searchstring: ['Water'], + Invert: true + }, + { + PokemonID: 208, + Name: "only Steel", + Searchstring: ['Steel'], + Invert: true + }, + { + PokemonID: 25, + Name: "only Electric", + Searchstring: ['Electric'], + Invert: true + }, + { + PokemonID: 4, + Name: "only Fire", + Searchstring: ['Fire'], + Invert: true + }, + { + PokemonID: 198, + Name: "only Dark", + Searchstring: ['Dark'], + Invert: true + }, + { + PokemonID: 10, + Name: "only Bug", + Searchstring: ['Bug'], + Invert: true + }, + { + PokemonID: 16, + Name: "only Normal", + Searchstring: ['Normal'], + Invert: true + }, + { + PokemonID: 66, + Name: "only Fight", + Searchstring: ['Fighting'], + Invert: true + }, + { + PokemonID: 92, + Name: "only Ghost", + Searchstring: ['Ghost'], + Invert: true + }, + { + PokemonID: 63, + Name: "only Psychic", + Searchstring: ['Psychic'], + Invert: true + }, + { + PokemonID: 12, + Name: "only Flying", + Searchstring: ['Flying'], + Invert: true + }, + { + PokemonID: 1, + Name: "only Grass", + Searchstring: ['Grass'], + Invert: true + }, + { + PokemonID: 29, + Name: "only Poison", + Searchstring: ['Poison'], + Invert: true + }, + { + PokemonID: 35, + Name: "only Fairy", + Searchstring: ['Fairy'], + Invert: true + }, + { + PokemonID: 364, + Name: "only Ice", + Searchstring: ['Ice'], + Invert: true + }, + { + PokemonID: 1, + Name: "Generation 1", + Searchstring: ['gen1'], + Invert: true + }, + { + PokemonID: 152, + Name: "Generation 2", + Searchstring: ['gen2'], + Invert: true + }, + { + PokemonID: 252, + Name: "Generation 3", + Searchstring: ['gen3'], + Invert: true + } + ] + + // MOTD. const motdEnabled = false const motdTitle = 'MOTD' @@ -42,11 +162,19 @@ $(function () { const clusterGridSize = 60 // Default: 60. const clusterGridSizeMobile = 60 // Default: 60. + // Rarities Sprites + const rarityCommon = 12 // Default: 1. + const rarityUncommon = 20 // Default: 1. + const rarityRare = 100 // Default: 1. + const rarityVeryRare = 147 // Default: 1. + const rarityUltraRare = 149 // Default: 1. + const rarityNewSpawn = 151 // Default: 1. + // Process Pokémon in chunks to improve responsiveness. const processPokemonChunkSize = 100 // Default: 100 const processPokemonIntervalMs = 100 // Default: 100ms - const processPokemonChunkSizeMobile = 100 // Default: 100. - const processPokemonIntervalMsMobile = 100 // Default: 100ms. + const processPokemonChunkSizeMobile = 100 // Default: 100. + const processPokemonIntervalMsMobile = 100 // Default: 100ms. /* Feature detection. */ @@ -77,16 +205,25 @@ $(function () { Store.set('scaleByRarity', scaleByRarity) Store.set('upscalePokemon', upscalePokemon) Store.set('upscaledPokemon', upscaledPokemon) - Store.set('showSearchMarker', showSearchMarker) - Store.set('isSearchMarkerMovable', isSearchMarkerMovable) + Store.set('showSearchMarker', showSearchMarker) + Store.set('isSearchMarkerMovable', isSearchMarkerMovable) Store.set('showLocationMarker', showLocationMarker) Store.set('isLocationMarkerMovable', isLocationMarkerMovable) + Store.set('hidepresets', hidepresets) + + // Set rarities sprites + Store.set('rarityCommon', rarityCommon) + Store.set('rarityUncommon', rarityUncommon) + Store.set('rarityRare', rarityRare) + Store.set('rarityVeryRare', rarityVeryRare) + Store.set('rarityUltraRare', rarityUltraRare) + Store.set('rarityNewSpawn', rarityNewSpawn) if (typeof window.orientation !== 'undefined' || isMobileDevice()) { Store.set('maxClusterZoomLevel', maxClusterZoomLevelMobile) Store.set('clusterZoomOnClick', clusterZoomOnClickMobile) Store.set('clusterGridSize', clusterGridSizeMobile) - Store.set('processPokemonChunkSize', processPokemonChunkSizeMobile) + Store.set('processPokemonChunkSize', processPokemonChunkSizeMobile) Store.set('processPokemonIntervalMs', processPokemonIntervalMsMobile) } diff --git a/static/js/map.common.js b/static/js/map.common.js index 7887b34f9..e49081723 100644 --- a/static/js/map.common.js +++ b/static/js/map.common.js @@ -1,802 +1,17 @@ -/*global i8ln*/ +/*global i8ln, L, markers, markersnotify*/ /* eslint no-unused-vars: "off" */ +function pokemonSprites(pokemonID) { + var sprite = { + columns: 28, + iconWidth: 80, + iconHeight: 80, + spriteWidth: 2240, + spriteHeight: 1440, + filename: 'static/icons/' + pokemonID + '.png', + name: 'High-Res' + } -var noLabelsStyle = [{ - featureType: 'poi', - elementType: 'labels', - stylers: [{ - visibility: 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.fill', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}] -var light2Style = [{ - 'elementType': 'geometry', - 'stylers': [{ - 'hue': '#ff4400' - }, { - 'saturation': -68 - }, { - 'lightness': -4 - }, { - 'gamma': 0.72 - }] -}, { - 'featureType': 'road', - 'elementType': 'labels.icon' -}, { - 'featureType': 'landscape.man_made', - 'elementType': 'geometry', - 'stylers': [{ - 'hue': '#0077ff' - }, { - 'gamma': 3.1 - }] -}, { - 'featureType': 'water', - 'stylers': [{ - 'hue': '#00ccff' - }, { - 'gamma': 0.44 - }, { - 'saturation': -33 - }] -}, { - 'featureType': 'poi.park', - 'stylers': [{ - 'hue': '#44ff00' - }, { - 'saturation': -23 - }] -}, { - 'featureType': 'water', - 'elementType': 'labels.text.fill', - 'stylers': [{ - 'hue': '#007fff' - }, { - 'gamma': 0.77 - }, { - 'saturation': 65 - }, { - 'lightness': 99 - }] -}, { - 'featureType': 'water', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'gamma': 0.11 - }, { - 'weight': 5.6 - }, { - 'saturation': 99 - }, { - 'hue': '#0091ff' - }, { - 'lightness': -86 - }] -}, { - 'featureType': 'transit.line', - 'elementType': 'geometry', - 'stylers': [{ - 'lightness': -48 - }, { - 'hue': '#ff5e00' - }, { - 'gamma': 1.2 - }, { - 'saturation': -23 - }] -}, { - 'featureType': 'transit', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'saturation': -64 - }, { - 'hue': '#ff9100' - }, { - 'lightness': 16 - }, { - 'gamma': 0.47 - }, { - 'weight': 2.7 - }] -}] -var darkStyle = [{ - 'featureType': 'all', - 'elementType': 'labels.text.fill', - 'stylers': [{ - 'saturation': 36 - }, { - 'color': '#b39964' - }, { - 'lightness': 40 - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'visibility': 'on' - }, { - 'color': '#000000' - }, { - 'lightness': 16 - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'administrative', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 20 - }] -}, { - 'featureType': 'administrative', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 17 - }, { - 'weight': 1.2 - }] -}, { - 'featureType': 'landscape', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 20 - }] -}, { - 'featureType': 'poi', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 21 - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 17 - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 29 - }, { - 'weight': 0.2 - }] -}, { - 'featureType': 'road.arterial', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 18 - }] -}, { - 'featureType': 'road.local', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#181818' - }, { - 'lightness': 16 - }] -}, { - 'featureType': 'transit', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 19 - }] -}, { - 'featureType': 'water', - 'elementType': 'geometry', - 'stylers': [{ - 'lightness': 17 - }, { - 'color': '#525252' - }] -}] -var pGoStyle = [{ - 'featureType': 'landscape.man_made', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#a1f199' - }] -}, { - 'featureType': 'landscape.natural.landcover', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#37bda2' - }] -}, { - 'featureType': 'landscape.natural.terrain', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#37bda2' - }] -}, { - 'featureType': 'poi.attraction', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'visibility': 'on' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#e4dfd9' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'poi.park', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#37bda2' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#84b09e' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#fafeb8' - }, { - 'weight': '1.25' - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'water', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#5ddad6' - }] -}] -var light2StyleNoLabels = [{ - 'elementType': 'geometry', - 'stylers': [{ - 'hue': '#ff4400' - }, { - 'saturation': -68 - }, { - 'lightness': -4 - }, { - 'gamma': 0.72 - }] -}, { - 'featureType': 'road', - 'elementType': 'labels.icon' -}, { - 'featureType': 'landscape.man_made', - 'elementType': 'geometry', - 'stylers': [{ - 'hue': '#0077ff' - }, { - 'gamma': 3.1 - }] -}, { - 'featureType': 'water', - 'stylers': [{ - 'hue': '#00ccff' - }, { - 'gamma': 0.44 - }, { - 'saturation': -33 - }] -}, { - 'featureType': 'poi.park', - 'stylers': [{ - 'hue': '#44ff00' - }, { - 'saturation': -23 - }] -}, { - 'featureType': 'water', - 'elementType': 'labels.text.fill', - 'stylers': [{ - 'hue': '#007fff' - }, { - 'gamma': 0.77 - }, { - 'saturation': 65 - }, { - 'lightness': 99 - }] -}, { - 'featureType': 'water', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'gamma': 0.11 - }, { - 'weight': 5.6 - }, { - 'saturation': 99 - }, { - 'hue': '#0091ff' - }, { - 'lightness': -86 - }] -}, { - 'featureType': 'transit.line', - 'elementType': 'geometry', - 'stylers': [{ - 'lightness': -48 - }, { - 'hue': '#ff5e00' - }, { - 'gamma': 1.2 - }, { - 'saturation': -23 - }] -}, { - 'featureType': 'transit', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'saturation': -64 - }, { - 'hue': '#ff9100' - }, { - 'lightness': 16 - }, { - 'gamma': 0.47 - }, { - 'weight': 2.7 - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.fill', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}] -var darkStyleNoLabels = [{ - 'featureType': 'all', - 'elementType': 'labels.text.fill', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'administrative', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 20 - }] -}, { - 'featureType': 'administrative', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 17 - }, { - 'weight': 1.2 - }] -}, { - 'featureType': 'landscape', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 20 - }] -}, { - 'featureType': 'poi', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 21 - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 17 - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 29 - }, { - 'weight': 0.2 - }] -}, { - 'featureType': 'road.arterial', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 18 - }] -}, { - 'featureType': 'road.local', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#181818' - }, { - 'lightness': 16 - }] -}, { - 'featureType': 'transit', - 'elementType': 'geometry', - 'stylers': [{ - 'color': '#000000' - }, { - 'lightness': 19 - }] -}, { - 'featureType': 'water', - 'elementType': 'geometry', - 'stylers': [{ - 'lightness': 17 - }, { - 'color': '#525252' - }] -}] -var pGoStyleNoLabels = [{ - 'featureType': 'landscape.man_made', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#a1f199' - }] -}, { - 'featureType': 'landscape.natural.landcover', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#37bda2' - }] -}, { - 'featureType': 'landscape.natural.terrain', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#37bda2' - }] -}, { - 'featureType': 'poi.attraction', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'visibility': 'on' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#e4dfd9' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'poi.park', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#37bda2' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#84b09e' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#fafeb8' - }, { - 'weight': '1.25' - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'water', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#5ddad6' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.stroke', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.text.fill', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'all', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}] -var pGoStyleDay = [{ - 'featureType': 'landscape.man_made', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#99f291' - }] -}, { - 'featureType': 'landscape.natural.landcover', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#00af8f' - }] -}, { - 'featureType': 'landscape.natural.terrain', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#00af8f' - }] -}, { - 'featureType': 'landscape.natural', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#00af8f' - }] -}, { - 'featureType': 'poi.attraction', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'visibility': 'on' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#e4dfd9' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'poi.park', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#00af8f' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#7eb2a4' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#ffff92' - }, { - 'weight': '2' - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'water', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#1688da' - }] -}, { - 'featureType': 'poi.attraction', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#e4fdee' - }] -}, { - 'featureType': 'poi.sports_complex', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#d4ffbc' - }] -}] -var pGoStyleNight = [{ - 'featureType': 'landscape.man_made', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#12a085' - }] -}, { - 'featureType': 'landscape.natural.landcover', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#02706a' - }] -}, { - 'featureType': 'landscape.natural.terrain', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#02706a' - }] -}, { - 'featureType': 'landscape.natural', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#02706a' - }] -}, { - 'featureType': 'poi', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#6da298' - }] -}, { - 'featureType': 'poi.medical', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#6da298' - }] -}, { - 'featureType': 'poi.attraction', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'visibility': 'on' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#1fba9c' - }] -}, { - 'featureType': 'poi.business', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'poi.park', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#02706a' - }] -}, { - 'featureType': 'transit', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#428290' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#316589' - }] -}, { - 'featureType': 'road', - 'elementType': 'geometry.stroke', - 'stylers': [{ - 'color': '#7f8b60' - }, { - 'weight': '2' - }] -}, { - 'featureType': 'road.highway', - 'elementType': 'labels.icon', - 'stylers': [{ - 'visibility': 'off' - }] -}, { - 'featureType': 'water', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#1e4fbc' - }] -}, { - 'featureType': 'poi.attraction', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#1fba9c' - }] -}, { - 'featureType': 'poi.sports_complex', - 'elementType': 'geometry.fill', - 'stylers': [{ - 'color': '#1fba9c' - }] -}] - -var pokemonSprites = { - columns: 28, - iconWidth: 80, - iconHeight: 80, - spriteWidth: 2240, - spriteHeight: 1440, - filename: 'static/icons-large-sprite.png', - name: 'High-Res' + return sprite } // @@ -848,7 +63,7 @@ var StoreTypes = { // set the default parameters for you map here var StoreOptions = { 'map_style': { - default: 'roadmap', // roadmap, satellite, hybrid, nolabels_style, dark_style, style_light2, style_pgo, dark_style_nl, style_pgo_day, style_pgo_night, style_pgo_dynamic + default: 'stylemapnik', // stylemapnik, styleblackandwhite, styletopo, stylesatellite, stylewikipedia type: StoreTypes.String }, 'remember_select_exclude': { @@ -1083,6 +298,10 @@ var StoreOptions = { default: false, type: StoreTypes.Boolean }, + 'hidepresets': { + default: [], + type: StoreTypes.JSON + }, 'showMedalRattata': { default: false, type: StoreTypes.Boolean @@ -1090,7 +309,32 @@ var StoreOptions = { 'showMedalMagikarp': { default: false, type: StoreTypes.Boolean + }, + 'rarityCommon': { + default: 1, + type: StoreTypes.Number + }, + 'rarityUncommon': { + default: 1, + type: StoreTypes.Number + }, + 'rarityRare': { + default: 1, + type: StoreTypes.Number + }, + 'rarityVeryRare': { + default: 1, + type: StoreTypes.Number + }, + 'rarityUltraRare': { + default: 1, + type: StoreTypes.Number + }, + 'rarityNewSpawn': { + default: 1, + type: StoreTypes.Number } + } var Store = { @@ -1137,9 +381,9 @@ var mapData = { function getPokemonIcon(item, sprite, displayHeight) { displayHeight = Math.max(displayHeight, 3) var scale = displayHeight / sprite.iconHeight - var scaledIconSize = new google.maps.Size(scale * sprite.iconWidth, scale * sprite.iconHeight) - var scaledIconOffset = new google.maps.Point(0, 0) - var scaledIconCenterOffset = new google.maps.Point(scale * sprite.iconWidth / 2, scale * sprite.iconHeight / 2) + var scaledIconSize = [scale * sprite.iconWidth, scale * sprite.iconHeigt] + var scaledIconOffset = [0, 0] + var scaledIconCenterOffset = [scale * sprite.iconWidth / 2, scale * sprite.iconHeight / 2] let genderParam = item['gender'] ? `&gender=${item['gender']}` : '' let formParam = item['form'] ? `&form=${item['form']}` : '' @@ -1148,11 +392,9 @@ function getPokemonIcon(item, sprite, displayHeight) { let iconUrl = `pkm_img?pkm=${item['pokemon_id']}${genderParam}${formParam}${costumeParam}${weatherParam}` return { - url: iconUrl, - size: scaledIconSize, - scaledSize: scaledIconSize, - origin: scaledIconOffset, - anchor: scaledIconCenterOffset + iconUrl: iconUrl, + iconSize: scaledIconSize, + iconAnchor: scaledIconCenterOffset } } @@ -1188,30 +430,30 @@ function getGoogleSprite(index, sprite, displayHeight) { displayHeight = Math.max(displayHeight, 3) var scale = displayHeight / sprite.iconHeight // Crop icon just a tiny bit to avoid bleedover from neighbor - var scaledIconSize = new google.maps.Size(scale * sprite.iconWidth - 1, scale * sprite.iconHeight - 1) - var scaledIconOffset = new google.maps.Point( + var scaledIconSize = (scale * sprite.iconWidth - 1, scale * sprite.iconHeight - 1) + var scaledIconOffset = ( (index % sprite.columns) * sprite.iconWidth * scale + 0.5, Math.floor(index / sprite.columns) * sprite.iconHeight * scale + 0.5) - var scaledSpriteSize = new google.maps.Size(scale * sprite.spriteWidth, scale * sprite.spriteHeight) - var scaledIconCenterOffset = new google.maps.Point(scale * sprite.iconWidth / 2, scale * sprite.iconHeight / 2) + var scaledSpriteSize = (scale * sprite.spriteWidth, scale * sprite.spriteHeight) + var scaledIconCenterOffset = (scale * sprite.iconWidth / 2, scale * sprite.iconHeight / 2) return { - url: sprite.filename, - size: scaledIconSize, + iconUrl: sprite.filename, + iconSize: scaledIconSize, + iconAnchor: scaledIconCenterOffset, scaledSize: scaledSpriteSize, - origin: scaledIconOffset, - anchor: scaledIconCenterOffset + origin: scaledIconOffset } } function setupPokemonMarkerDetails(item, map, scaleByRarity = true, isNotifyPkmn = false) { const pokemonIndex = item['pokemon_id'] - 1 - const sprite = pokemonSprites + const sprite = pokemonSprites(pokemonIndex) var markerDetails = { sprite: sprite } - var iconSize = (map.getZoom() - 3) * (map.getZoom() - 3) * 0.2 + Store.get('iconSizeModifier') + var iconSize = (12) * (12) * 0.2 + Store.get('iconSizeModifier') rarityValue = 2 if (Store.get('upscalePokemon')) { @@ -1221,13 +463,13 @@ function setupPokemonMarkerDetails(item, map, scaleByRarity = true, isNotifyPkmn if (scaleByRarity) { const rarityValues = { - 'new spawn': 40, - 'very rare': 30, - 'ultra rare': 40, - 'legendary': 50 + 'new spawn': 20, + 'very rare': 20, + 'ultra rare': 25, + 'legendary': 30 } - const pokemonRarity = getPokemonRarityNoi8(item['pokemon_id']).toLowerCase() + const pokemonRarity = getPokemonRarity(item['pokemon_id']).toLowerCase() if (rarityValues.hasOwnProperty(pokemonRarity)) { rarityValue = rarityValues[pokemonRarity] } @@ -1243,33 +485,29 @@ function setupPokemonMarkerDetails(item, map, scaleByRarity = true, isNotifyPkmn return markerDetails } -function setupPokemonMarker(item, map, isBounceDisabled, scaleByRarity = true, isNotifyPkmn = false) { +function updatePokemonMarker(item, map, scaleByRarity = true, isNotifyPkmn = false) { // Scale icon size up with the map exponentially, also size with rarity. const markerDetails = setupPokemonMarkerDetails(item, map, scaleByRarity, isNotifyPkmn) - const icon = markerDetails.icon - - var marker = new google.maps.Marker({ - position: { - lat: item['latitude'], - lng: item['longitude'] - }, - zIndex: 9949 + markerDetails.rarityValue, - icon: icon, - animationDisabled: isBounceDisabled - }) + const icon = L.icon(markerDetails.icon) + const marker = item.marker - return marker + marker.setIcon(icon) } -function updatePokemonMarker(item, map, scaleByRarity = true, isNotifyPkmn = false) { +function setupPokemonMarker(item, map, isBounceDisabled, scaleByRarity = true, isNotifyPkmn = false) { // Scale icon size up with the map exponentially, also size with rarity. const markerDetails = setupPokemonMarkerDetails(item, map, scaleByRarity, isNotifyPkmn) - const icon = markerDetails.icon - const marker = item.marker - - marker.setIcon(icon) + const icon = L.icon(markerDetails.icon) + var pokemonMarker + if (!isNotifyPkmn) { + pokemonMarker = L.marker([item['latitude'], item['longitude']], {icon: icon, zIndexOffset: 100 + markerDetails.rarityValue}).addTo(markers) + } else { + pokemonMarker = L.marker([item['latitude'], item['longitude']], {icon: icon, zIndexOffset: 1000 + markerDetails.rarityValue}).addTo(markersnotify) + } + return pokemonMarker } + function updatePokemonLabel(item) { // Only update label when Pokémon has been encountered. if (item['cp'] !== null && item['cpMultiplier'] !== null) { diff --git a/static/js/map.js b/static/js/map.js index 1641d8cc8..a72b476f5 100644 --- a/static/js/map.js +++ b/static/js/map.js @@ -24,6 +24,7 @@ var $selectSearchIconMarker var $selectLocationIconMarker var $switchGymSidebar var $selectExcludeRarity +var pokeSearchList = [] var pokemonGen = new Array(808) pokemonGen.fill(1, 1, 152) pokemonGen.fill(2, 152, 252) @@ -55,7 +56,7 @@ var buffer = [] var reincludedPokemon = [] var reids = [] -var map +// var map var markerCluster = window.markerCluster = {} var rawDataIsLoading = false var locationMarker @@ -69,13 +70,19 @@ var oSwLng var oNeLat var oNeLng +var L +var map +var markers +var markersnotify +var _oldlayer = 'stylemapnik' + var lastpokestops var lastgyms var lastpokemon var lastslocs var lastspawns -var selectedStyle = 'light' +var selectedStyle = 'stylemapnik' var updateWorker var lastUpdateTime @@ -171,30 +178,24 @@ function toggleSelectItem($select, id) { } function excludePokemon(id, encounterId) { // eslint-disable-line no-unused-vars - toggleSelectItem($selectExclude, id) - var pkm = mapData.pokemons[encounterId] - pkm.marker.infoWindow.setContent(pokemonLabel(pkm)) + $('label[for="exclude-pokemon"] .list .pokemon-icon-sprite[data-value="' + id + '"]').click() } function notifyAboutPokemon(id, encounterId) { // eslint-disable-line no-unused-vars - toggleSelectItem($selectPokemonNotify, id) - var pkm = mapData.pokemons[encounterId] - pkm.marker.infoWindow.setContent(pokemonLabel(pkm)) + $('label[for="notify-pokemon"] .list .pokemon-icon-sprite[data-value="' + id + '"]').click() } function removePokemonMarker(encounterId) { // eslint-disable-line no-unused-vars if (mapData.pokemons[encounterId].marker.rangeCircle) { - mapData.pokemons[encounterId].marker.rangeCircle.setMap(null) + markers.removeLayer(mapData.pokemons[encounterId].marker.rangeCircle) + markersnotify.removeLayer(mapData.pokemons[encounterId].marker.rangeCircle) delete mapData.pokemons[encounterId].marker.rangeCircle } if (mapData.pokemons[encounterId].marker.infoWindowIsOpen) { - mapData.pokemons[encounterId].marker.persist = null - mapData.pokemons[encounterId].marker.infoWindow.close() mapData.pokemons[encounterId].marker.infoWindowIsOpen = false } - mapData.pokemons[encounterId].marker.setMap(null) - mapData.pokemons[encounterId].marker.setVisible(false) - mapData.pokemons[encounterId].hidden = true + markers.removeLayer(mapData.pokemons[encounterId].marker) + markersnotify.removeLayer(mapData.pokemons[encounterId].marker) } function createServiceWorkerReceiver() { @@ -225,110 +226,77 @@ function loadSettingsFile(file) { // eslint-disable-line no-unused-vars window.location.reload() } -function initMap() { // eslint-disable-line no-unused-vars - map = new google.maps.Map(document.getElementById('map'), { - center: { - lat: Number(getParameterByName('lat')) || centerLat, - lng: Number(getParameterByName('lon')) || centerLng - }, - zoom: Number(getParameterByName('zoom')) || Store.get('zoomLevel'), - gestureHandling: 'greedy', - fullscreenControl: true, - streetViewControl: false, - mapTypeControl: false, - clickableIcons: false, - mapTypeControlOptions: { - style: google.maps.MapTypeControlStyle.DROPDOWN_MENU, - position: google.maps.ControlPosition.RIGHT_TOP, - mapTypeIds: [ - google.maps.MapTypeId.ROADMAP, - google.maps.MapTypeId.SATELLITE, - google.maps.MapTypeId.HYBRID, - 'nolabels_style', - 'dark_style', - 'style_light2', - 'style_pgo', - 'dark_style_nl', - 'style_light2_nl', - 'style_pgo_nl', - 'style_pgo_day', - 'style_pgo_night', - 'style_pgo_dynamic' - ] +function loadDefaultImages() { + var ep = Store.get('remember_select_exclude') + var en = Store.get('remember_select_notify') + $('label[for="exclude-pokemon"] .list .pokemon-icon-sprite').removeClass('active') + $('label[for="exclude-pokemon"] .list .pokemon-icon-sprite').each(function () { + if (ep.indexOf($(this).data('value')) !== -1) { + $(this).addClass('active') + $('.hidefilteractiv').css('color', 'red') + $('.hidefilteractiv').text('*** active Filter ***') } }) - - // Enable clustering. - var clusterOptions = { - imagePath: 'static/images/cluster/m', - maxZoom: Store.get('maxClusterZoomLevel'), - zoomOnClick: Store.get('clusterZoomOnClick'), - gridSize: Store.get('clusterGridSize') - } - - markerCluster = new MarkerClusterer(map, [], clusterOptions) - - var styleNoLabels = new google.maps.StyledMapType(noLabelsStyle, { - name: 'No Labels' - }) - map.mapTypes.set('nolabels_style', styleNoLabels) - - var styleDark = new google.maps.StyledMapType(darkStyle, { - name: 'Dark' + $('label[for="notify-pokemon"] .list .pokemon-icon-sprite').removeClass('active') + $('label[for="notify-pokemon"] .list .pokemon-icon-sprite').each(function () { + if (en.indexOf($(this).data('value')) !== -1) { + $(this).addClass('active') + $('.notifyfilteractiv').css('color', 'red') + $('.notifyfilteractiv').text('*** active Filter ***') + } }) - map.mapTypes.set('dark_style', styleDark) +} - var styleLight2 = new google.maps.StyledMapType(light2Style, { - name: 'Light2' - }) - map.mapTypes.set('style_light2', styleLight2) - var stylePgo = new google.maps.StyledMapType(pGoStyle, { - name: 'RocketMap' +function initMap() { // eslint-disable-line no-unused-vars + map = L.map('map', { + center: [Number(getParameterByName('lat')) || centerLat, Number(getParameterByName('lon')) || centerLng], + zoom: Number(getParameterByName('zoom')) || Store.get('zoomLevel'), + maxZoom: 18, + zoomControl: false }) - map.mapTypes.set('style_pgo', stylePgo) - var styleDarkNl = new google.maps.StyledMapType(darkStyleNoLabels, { - name: 'Dark (No Labels)' - }) - map.mapTypes.set('dark_style_nl', styleDarkNl) + setTitleLayer(Store.get('map_style')) - var styleLight2Nl = new google.maps.StyledMapType(light2StyleNoLabels, { - name: 'Light2 (No Labels)' + markers = L.markerClusterGroup({ + disableClusteringAtZoom: Store.get('maxClusterZoomLevel'), + spiderfyOnMaxZoom: false, + zoomToBoundsOnClick: Store.get('clusterZoomOnClick'), + showCoverageOnHover: false, + maxClusterRadius: Store.get('clusterGridSize'), + removeOutsideVisibleBounds: true }) - map.mapTypes.set('style_light2_nl', styleLight2Nl) - var stylePgoNl = new google.maps.StyledMapType(pGoStyleNoLabels, { - name: 'RocketMap (No Labels)' - }) - map.mapTypes.set('style_pgo_nl', stylePgoNl) + L.control.zoom({ + position: 'bottomright' + }).addTo(map) - var stylePgoDay = new google.maps.StyledMapType(pGoStyleDay, { - name: 'RocketMap Day' - }) - map.mapTypes.set('style_pgo_day', stylePgoDay) + map.addLayer(markers) + markersnotify = L.layerGroup().addTo(map) - var stylePgoNight = new google.maps.StyledMapType(pGoStyleNight, { - name: 'RocketMap Night' - }) - map.mapTypes.set('style_pgo_night', stylePgoNight) + if (showConfig.fixed_display) { + var GeoSearchControl = window.GeoSearch.GeoSearchControl + var OpenStreetMapProvider = window.GeoSearch.OpenStreetMapProvider + var provider = new OpenStreetMapProvider() - // dynamic map style chooses stylePgoDay or stylePgoNight depending on client time - var currentDate = new Date() - var currentHour = currentDate.getHours() - var stylePgoDynamic = (currentHour >= 6 && currentHour < 19) ? stylePgoDay : stylePgoNight - map.mapTypes.set('style_pgo_dynamic', stylePgoDynamic) + const search = new GeoSearchControl({ + provider: provider, + position: 'bottomright', + autoClose: true, + keepResult: false, + showMarker: false + }) - map.addListener('maptypeid_changed', function (s) { - Store.set('map_style', this.mapTypeId) - }) + map.addControl(search) - map.setMapTypeId(Store.get('map_style')) - map.addListener('idle', updateMap) + map.on('geosearch/showlocation', function (e) { + changeLocation(e.location.y, e.location.x) + }) + } - map.addListener('zoom_changed', function () { + map.on('zoom', function () { if (storeZoom === true) { - Store.set('zoomLevel', this.getZoom()) + Store.set('zoomLevel', map.getZoom()) } else { storeZoom = true } @@ -338,7 +306,6 @@ function initMap() { // eslint-disable-line no-unused-vars clearTimeout(redrawTimeout) redrawTimeout = null } - // Don't redraw constantly even if the user scrolls multiple times, // just add it on a timer. redrawTimeout = setTimeout(function () { @@ -346,7 +313,7 @@ function initMap() { // eslint-disable-line no-unused-vars redrawPokemon(mapData.lurePokemons) // We're done processing the list. Repaint. - markerCluster.repaint() + markers.refreshClusters() }, 500) }) @@ -361,15 +328,14 @@ function initMap() { // eslint-disable-line no-unused-vars if (showLocationMarker) { locationMarker = createLocationMarker() - locationMarker.setDraggable(isLocationMarkerMovable) + locationMarker.draggable = isLocationMarkerMovable } - createMyLocationButton() initSidebar() $('#scan-here').on('click', function () { var loc = map.getCenter() - changeLocation(loc.lat(), loc.lng()) + changeLocation(loc.lat, loc.lng) if (!$('#search-switch').checked) { $('#search-switch').prop('checked', true) @@ -377,25 +343,43 @@ function initMap() { // eslint-disable-line no-unused-vars } }) + $('#tabs_marker').tabs() + $('#tabs_notify').tabs() + if (Push._agents.chrome.isSupported()) { createServiceWorkerReceiver() } } +var stylemapnik = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap'}) +var styleblackandwhite = L.tileLayer('https://korona.geog.uni-heidelberg.de/tiles/roadsg/x={x}&y={y}&z={z}', {attribution: 'Imagery from GIScience Research Group @ University of Heidelberg — Map data © OpenStreetMap'}) +var styletopo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap'}) +var stylesatellite = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'}) +var stylewikipedia = L.tileLayer('https://maps.wikimedia.org/osm-intl/{z}/{x}/{y}{r}.png', {attribution: 'Wikimedia'}) + +function setTitleLayer(layername) { + if (map.hasLayer(window[_oldlayer])) { map.removeLayer(window[_oldlayer]) } + map.addLayer(window[layername]) + _oldlayer = layername +} + function updateLocationMarker(style) { // Don't do anything if it's disabled. if (!locationMarker) { return } + var locationIcon if (style in searchMarkerStyles) { var url = searchMarkerStyles[style].icon if (url) { - locationMarker.setIcon({ - url: url, - scaledSize: new google.maps.Size(24, 24) + locationIcon = L.icon({ + iconUrl: url, + iconSize: [24, 24] }) + locationMarker.setIcon(locationIcon) } else { - locationMarker.setIcon(url) + locationIcon = new L.Icon.Default() + locationMarker.setIcon(locationIcon) } Store.set('locationMarkerStyle', style) } @@ -408,31 +392,14 @@ function createLocationMarker() { var lat = ('lat' in position) ? position.lat : centerLat var lng = ('lng' in position) ? position.lng : centerLng - var locationMarker = new google.maps.Marker({ - map: map, - animation: google.maps.Animation.DROP, - position: { - lat: lat, - lng: lng - }, - draggable: true, - icon: null, - optimized: false, - zIndex: google.maps.Marker.MAX_ZINDEX + 2 - }) - - locationMarker.infoWindow = new google.maps.InfoWindow({ - content: '
My Location
', - disableAutoPan: true - }) - + var locationMarker = L.marker([lat, lng]).addTo(markersnotify).bindPopup('
My Location
') addListeners(locationMarker) - google.maps.event.addListener(locationMarker, 'dragend', function () { - var newLocation = locationMarker.getPosition() + locationMarker.on('dragend', function () { + var newLocation = locationMarker.getLatLng() Store.set('followMyLocationPosition', { - lat: newLocation.lat(), - lng: newLocation.lng() + lat: newLocation.lat, + lng: newLocation.lng }) }) @@ -450,12 +417,14 @@ function updateSearchMarker(style) { var url = searchMarkerStyles[style].icon if (url) { - searchMarker.setIcon({ - url: url, - scaledSize: new google.maps.Size(24, 24) + var SearchIcon = L.icon({ + iconUrl: url, + iconSize: [24, 24] }) + searchMarker.setIcon(SearchIcon) } else { - searchMarker.setIcon(url) + SearchIcon = new L.Icon.Default() + searchMarker.setIcon(SearchIcon) } } @@ -464,40 +433,22 @@ function updateSearchMarker(style) { function createSearchMarker() { const isSearchMarkerMovable = Store.get('isSearchMarkerMovable') - var searchMarker = new google.maps.Marker({ // need to keep reference. - position: { - lat: centerLat, - lng: centerLng - }, - map: map, - animation: google.maps.Animation.DROP, - draggable: !Store.get('lockMarker') && isSearchMarkerMovable, - icon: null, - optimized: false, - zIndex: google.maps.Marker.MAX_ZINDEX + 1 - }) - - searchMarker.infoWindow = new google.maps.InfoWindow({ - content: '
Search Location
', - disableAutoPan: true - }) - + var searchMarker = L.marker([centerLat, centerLng], {draggable: !Store.get('lockMarker') && isSearchMarkerMovable}).addTo(markersnotify).bindPopup('
Search Location
') addListeners(searchMarker) - var oldLocation = null - google.maps.event.addListener(searchMarker, 'dragstart', function () { - oldLocation = searchMarker.getPosition() + searchMarker.on('dragstart', function () { + oldLocation = searchMarker.getLatLng() }) - google.maps.event.addListener(searchMarker, 'dragend', function () { - var newLocation = searchMarker.getPosition() - changeSearchLocation(newLocation.lat(), newLocation.lng()) + searchMarker.on('dragend', function () { + var newLocation = searchMarker.getLatLng() + changeSearchLocation(newLocation.lat, newLocation.lng) .done(function () { oldLocation = null }) .fail(function () { if (oldLocation) { - searchMarker.setPosition(oldLocation) + searchMarker.setLatLng(oldLocation) } }) }) @@ -564,23 +515,6 @@ function initSidebar() { $('#medal-magikarp-switch').prop('checked', Store.get('showMedalMagikarp')) - // Only create the Autocomplete element if it's enabled in template. - var elSearchBox = document.getElementById('next-location') - - if (elSearchBox) { - var searchBox = new google.maps.places.Autocomplete(elSearchBox) - $(elSearchBox).css('background-color', $('#geoloc-switch').prop('checked') ? '#e0e0e0' : '#ffffff') - - searchBox.addListener('place_changed', function () { - var place = searchBox.getPlace() - - if (!place.geometry) return - - var loc = place.geometry.location - changeLocation(loc.lat(), loc.lng()) - }) - } - if ($('#search-switch').length) { updateSearchStatus() setInterval(updateSearchStatus, 5000) @@ -1119,7 +1053,10 @@ function spawnpointLabel(item) { function addRangeCircle(marker, map, type, teamId) { var targetmap = null - var circleCenter = new google.maps.LatLng(marker.position.lat(), marker.position.lng()) + var markerPos = marker.getLatLng() + var lat = markerPos.lat + var lng = markerPos.lng + var circleCenter = L.latLng(lat, lng) var gymColors = ['#999999', '#0051CF', '#FF260E', '#FECC23'] // 'Uncontested', 'Mystic', 'Valor', 'Instinct'] var teamColor = gymColors[0] if (teamId) teamColor = gymColors[teamId] @@ -1146,16 +1083,17 @@ function addRangeCircle(marker, map, type, teamId) { if (map) targetmap = map var rangeCircleOpts = { - map: targetmap, radius: range, // meters - strokeWeight: 1, - strokeColor: circleColor, - strokeOpacity: 0.9, + weight: 1, + color: circleColor, + opacity: 0.9, center: circleCenter, fillColor: circleColor, fillOpacity: 0.3 } - var rangeCircle = new google.maps.Circle(rangeCircleOpts) + + var rangeCircle = L.circle(circleCenter, rangeCircleOpts) + markers.addLayer(rangeCircle) return rangeCircle } @@ -1354,19 +1292,21 @@ function getNotifyPerfectionPokemons(pokemonList) { function customizePokemonMarker(marker, item, skipNotification) { var notifyText = getNotifyText(item) - marker.addListener('click', function () { - this.setAnimation(null) + marker.setBouncingOptions({ + bounceHeight: 20, // height of the bouncing + bounceSpeed: 80, // bouncing speed coefficient + elastic: false, + shadowAngle: null + }) + marker.on('mouseover', function () { + this.stopBouncing() this.animationDisabled = true }) if (!marker.rangeCircle && isRangeActive(map)) { marker.rangeCircle = addRangeCircle(marker, map, 'pokemon') } - - marker.infoWindow = new google.maps.InfoWindow({ - content: pokemonLabel(item), - disableAutoPan: true - }) + marker.bindPopup(pokemonLabel(item)) if (isNotifyPoke(item)) { if (!skipNotification) { @@ -1374,7 +1314,7 @@ function customizePokemonMarker(marker, item, skipNotification) { sendNotification(notifyText.fav_title, notifyText.fav_text, getPokemonRawIconUrl(item), item['latitude'], item['longitude']) } if (marker.animationDisabled !== true) { - marker.setAnimation(google.maps.Animation.BOUNCE) + marker.bounce() } } @@ -1382,26 +1322,15 @@ function customizePokemonMarker(marker, item, skipNotification) { } function setupGymMarker(item) { - var marker = new google.maps.Marker({ - position: { - lat: item['latitude'], - lng: item['longitude'] - }, - map: map - }) - marker.infoWindow = new google.maps.InfoWindow({ - content: '', - disableAutoPan: true - }) + var marker = L.marker([item['latitude'], item['longitude']]) + markers.addLayer(marker) updateGymMarker(item, marker) - if (!marker.rangeCircle && isRangeActive(map)) { marker.rangeCircle = addRangeCircle(marker, map, 'gym', item['team_id']) } - if (Store.get('useGymSidebar')) { - marker.addListener('click', function () { + marker.on('click', function () { var gymSidebar = document.querySelector('#gym-details') if (gymSidebar.getAttribute('data-id') === item['gym_id'] && gymSidebar.classList.contains('visible')) { gymSidebar.classList.remove('visible') @@ -1410,83 +1339,67 @@ function setupGymMarker(item) { showGymDetails(item['gym_id']) } }) - - google.maps.event.addListener(marker.infoWindow, 'closeclick', function () { - marker.persist = null - }) - + } else { if (!isMobileDevice() && !isTouchDevice()) { - marker.addListener('mouseover', function () { - marker.infoWindow.open(map, marker) + marker.on('mouseover', function () { + marker.openPopup() clearSelection() updateLabelDiffTime() }) } - - marker.addListener('mouseout', function () { - if (!marker.persist) { - marker.infoWindow.close() - } + marker.on('mouseout', function () { + marker.closePopup() + clearSelection() + updateLabelDiffTime() }) - } else { - addListeners(marker) } - return marker } function updateGymMarker(item, marker) { let raidLevel = getRaidLevel(item.raid) let markerImage = '' + var zIndexOffset if (item.raid && isOngoingRaid(item.raid) && Store.get('showRaids') && raidLevel >= Store.get('showRaidMinLevel') && raidLevel <= Store.get('showRaidMaxLevel')) { markerImage = 'gym_img?team=' + gymTypes[item.team_id] + '&level=' + getGymLevel(item) + '&raidlevel=' + item['raid']['level'] + '&pkm=' + item['raid']['pokemon_id'] - marker.setZIndex(google.maps.Marker.MAX_ZINDEX + 1) + zIndexOffset = 100 } else if (item.raid && item.raid.end > Date.now() && Store.get('showRaids') && !Store.get('showActiveRaidsOnly') && raidLevel >= Store.get('showRaidMinLevel') && raidLevel <= Store.get('showRaidMaxLevel')) { markerImage = 'gym_img?team=' + gymTypes[item.team_id] + '&level=' + getGymLevel(item) + '&raidlevel=' + item['raid']['level'] + zIndexOffset = 20 } else { markerImage = 'gym_img?team=' + gymTypes[item.team_id] + '&level=' + getGymLevel(item) - marker.setZIndex(1) + zIndexOffset = 10 } if (item['is_in_battle']) { markerImage += '&in_battle=1' } - marker.setIcon({ - url: markerImage, - scaledSize: new google.maps.Size(48, 48) + var GymIcon = new L.Icon({ + iconUrl: markerImage, + iconSize: [48, 48] }) - marker.infoWindow.setContent(gymLabel(item)) + marker.setIcon(GymIcon) + marker.setZIndexOffset = zIndexOffset + if (!Store.get('useGymSidebar')) { marker.bindPopup(gymLabel(item)) } return marker } function setupPokestopMarker(item) { var imagename = item['lure_expiration'] ? 'PokestopLured' : 'Pokestop' - var image = { - url: 'static/images/pokestop/' + imagename + '.png', - scaledSize: new google.maps.Size(32, 32) - } - var marker = new google.maps.Marker({ - position: { - lat: item['latitude'], - lng: item['longitude'] - }, - map: map, - zIndex: item['lure_expiration'] ? 3 : 2, - icon: image + var icon = L.icon({ + iconUrl: 'static/images/pokestop/' + imagename + '.png', + iconSize: [32, 32] }) + var marker = L.marker([item['latitude'], item['longitude']], {icon: icon, zIndexOffset: 5}).bindPopup(pokestopLabel(item['lure_expiration'], item['latitude'], item['longitude'])) + markers.addLayer(marker) if (!marker.rangeCircle && isRangeActive(map)) { marker.rangeCircle = addRangeCircle(marker, map, 'pokestop') } - marker.infoWindow = new google.maps.InfoWindow({ - content: pokestopLabel(item['lure_expiration'], item['latitude'], item['longitude']), - disableAutoPan: true - }) - - addListeners(marker) return marker } + function getColorByDate(value) { // Changes the color from red to green over 15 mins var diff = (Date.now() - value) / 1000 / 60 / 15 @@ -1501,20 +1414,19 @@ function getColorByDate(value) { } function setupScannedMarker(item) { - var circleCenter = new google.maps.LatLng(item['latitude'], item['longitude']) - - var marker = new google.maps.Circle({ - map: map, + var rangeCircleOpts = { clickable: false, - center: circleCenter, + center: [item['latitude'], item['longitude']], radius: (showConfig.pokemons === true ? 70 : 450), // metres - fillColor: getColorByDate(item['last_modified']), - fillOpacity: 0.1, + color: getColorByDate(item['last_modified']), + opacity: 0.1, strokeWeight: 1, strokeOpacity: 0.5 - }) + } - return marker + var circle = L.circle([item['latitude'], item['longitude']], rangeCircleOpts) + markersnotify.addLayer(circle) + return circle } function getColorBySpawnTime(value) { @@ -1529,6 +1441,7 @@ function getColorBySpawnTime(value) { } var diff = (seconds - value) + // hue, 100% saturation, 50% lightness var hue = 275 // light purple when spawn is neither about to spawn nor active if (diff >= 0 && diff <= 1800) { // green to red over 30 minutes of active spawn hue = (1 - (diff / 60 / 30)) * 120 @@ -1538,9 +1451,72 @@ function getColorBySpawnTime(value) { hue = Math.round(hue / 5) * 5 - return hue + return colourConversion.hsvToHex(hue, 1.0, 1.0) } +var colourConversion = (function () { + var self = {} + + self.hsvToHex = function (hue, sat, val) { + if (hue > 360 || hue < 0 || sat > 1 || sat < 0 || val > 1 || val < 0) { + console.log('{colourConverion.hsvToHex} illegal input') + return '#000000' + } + let rgbArray = hsvToRgb(hue, sat, val) + return rgbArrayToHexString(rgbArray) + } + + function rgbArrayToHexString(rgbArray) { + let hexString = '#' + for (var i = 0; i < rgbArray.length; i++) { + let hexOfNumber = rgbArray[i].toString(16) + if (hexOfNumber.length === 1) { + hexOfNumber = '0' + hexOfNumber + } + hexString += hexOfNumber + } + if (hexString.length !== 7) { + console.log('Hexstring not complete for colours...') + } + return hexString + } + + function mod(n, m) { + return ((n % m) + m) % m + } + + function hsvToRgb(hue, sat, val) { + let hder = Math.floor(hue / 60) + let f = hue / 60 - hder + let p = val * (1 - sat) + let q = val * (1 - sat * f) + let t = val * (1 - sat * (1 - f)) + var rgb + if (sat === 0) { + rgb = [val, val, val] + } else if (hder === 0 || hder === 6) { + rgb = [val, t, p] + } else if (hder === 1) { + rgb = [q, val, p] + } else if (hder === 2) { + rgb = [p, val, t] + } else if (hder === 3) { + rgb = [p, q, val] + } else if (hder === 4) { + rgb = [t, p, val] + } else if (hder === 5) { + rgb = [val, p, q] + } else { + console.log('Failed converting HSV to RGB') + } + for (var i = 0; i < rgb.length; i++) { + rgb[i] = Math.round(rgb[i] * 255) + } + return rgb + } + return self +})() + function changeSpawnIcon(color, zoom) { var urlColor = '' if (color === 275) { @@ -1555,11 +1531,11 @@ function changeSpawnIcon(color, zoom) { newSize = minimumSize } - var newIcon = { - url: urlColor, - scaledSize: new google.maps.Size(newSize, newSize), - anchor: new google.maps.Point(newSize / 2, newSize / 2) - } + var newIcon = L.icon({ + iconUrl: urlColor, + iconSize: [newSize, newSize], + iconAnchor: [newSize / 2, newSize / 2] + }) return newIcon } @@ -1578,27 +1554,26 @@ function spawnPointIndex(color) { return newIndex } +function rgbToHex(r, g, b) { + return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1) +} + function setupSpawnpointMarker(item) { - var circleCenter = new google.maps.LatLng(item['latitude'], item['longitude']) var hue = getColorBySpawnTime(item.appear_time) - var zoom = map.getZoom() - - var marker = new google.maps.Marker({ - map: map, - position: circleCenter, - icon: changeSpawnIcon(hue, zoom), - zIndex: spawnPointIndex(hue) - }) - - marker.infoWindow = new google.maps.InfoWindow({ - content: spawnpointLabel(item), - disableAutoPan: true, - position: circleCenter - }) - - addListeners(marker) + var rangeCircleOpts = { + radius: 4, // meters + weight: 1, + color: hue, + opacity: 1, + center: [item['latitude'], item['longitude']], + fillColor: hue, + fillOpacity: 1 + } - return marker + var circle = L.circle([item['latitude'], item['longitude']], rangeCircleOpts).bindPopup(spawnpointLabel(item)) + addListeners(circle) + markersnotify.addLayer(circle) + return circle } function clearSelection() { @@ -1610,35 +1585,31 @@ function clearSelection() { } function addListeners(marker) { - marker.addListener('click', function () { + marker.on('click', function () { if (!marker.infoWindowIsOpen) { - marker.infoWindow.open(map, marker) + marker.openPopup() clearSelection() updateLabelDiffTime() marker.persist = true marker.infoWindowIsOpen = true } else { marker.persist = null - marker.infoWindow.close() + marker.closePopup() marker.infoWindowIsOpen = false } }) - google.maps.event.addListener(marker.infoWindow, 'closeclick', function () { - marker.persist = null - }) - if (!isMobileDevice() && !isTouchDevice()) { - marker.addListener('mouseover', function () { - marker.infoWindow.open(map, marker) + marker.on('mouseover', function () { + marker.openPopup() clearSelection() updateLabelDiffTime() }) } - marker.addListener('mouseout', function () { + marker.on('mouseout', function () { if (!marker.persist) { - marker.infoWindow.close() + marker.closePopup() } }) @@ -1646,8 +1617,6 @@ function addListeners(marker) { } function clearStaleMarkers() { - const oldPokeMarkers = [] - $.each(mapData.pokemons, function (key, pokemon) { const pokemonId = pokemon['pokemon_id'] const isPokeExpired = pokemon['disappear_time'] < Date.now() @@ -1658,37 +1627,35 @@ function clearStaleMarkers() { const pokemonRarity = getPokemonRarity(pokemon['pokemon_id']) const isRarityExcluded = excludedRarity.indexOf(pokemonRarity) !== -1 const isNotifyPkmn = isNotifyPoke(pokemon) + var prionotifyactiv = Store.get('prioNotify') - if (isPokeExpired || (isPokeExcluded && !isNotifyPkmn) || (isRarityExcluded && !isNotifyPkmn)) { - const oldMarker = pokemon.marker - const isPokeExcludedByRarity = excludedPokemonByRarity.indexOf(pokemonId) !== -1 + if (isPokeExpired || isPokeExcluded || isRarityExcluded) { + if ((isNotifyPkmn && !prionotifyactiv) || (!isNotifyPkmn)) { + const oldMarker = pokemon.marker + const isPokeExcludedByRarity = excludedPokemonByRarity.indexOf(pokemonId) !== -1 - if (isRarityExcluded && !isPokeExcludedByRarity) { - excludedPokemonByRarity.push(pokemonId) - } + if (isRarityExcluded && !isPokeExcludedByRarity) { + excludedPokemonByRarity.push(pokemonId) + } - if (oldMarker.rangeCircle) { - oldMarker.rangeCircle.setMap(null) - delete oldMarker.rangeCircle - } + if (oldMarker.rangeCircle) { + markers.removeLayer(oldMarker.rangeCircle) + markersnotify.removeLayer(oldMarker.rangeCircle) + delete oldMarker.rangeCircle + } - // If it was a Pokémon w/ notification it won't be in a cluster, - // but that doesn't matter because the MarkerClusterer will check - // for it itself. - oldPokeMarkers.push(oldMarker) - oldMarker.setMap(null) - delete mapData.pokemons[key] - // Overwrite method to avoid all timing issues with libraries. - oldMarker.setMap = function () {} + markers.removeLayer(oldMarker) + markersnotify.removeLayer(oldMarker) + delete mapData.pokemons[key] + } } }) - markerCluster.removeMarkers(oldPokeMarkers, true) - $.each(mapData.lurePokemons, function (key, lurePokemon) { if (lurePokemon['lure_expiration'] < new Date().getTime() || getExcludedPokemon().indexOf(lurePokemon['pokemon_id']) >= 0) { - lurePokemon.marker.setMap(null) + markers.removeLayer(lurePokemon.marker) + markersnotify.removeLayer(lurePokemon.marker) delete mapData.lurePokemons[key] } }) @@ -1696,25 +1663,21 @@ function clearStaleMarkers() { $.each(mapData.scanned, function (key, scanned) { // If older than 15mins remove if (scanned['last_modified'] < (new Date().getTime() - 15 * 60 * 1000)) { - scanned.marker.setMap(null) + markersnotify.removeLayer(scanned.marker) delete mapData.scanned[key] } }) } -function showInBoundsMarkers(markers, type) { - $.each(markers, function (key, value) { - const item = markers[key] +function showInBoundsMarkers(markersInput, type) { + $.each(markersInput, function (key, value) { + const item = markersInput[key] const marker = item.marker var show = false if (!item.hidden) { - if (typeof marker.getBounds === 'function') { - if (map.getBounds().intersects(marker.getBounds())) { - show = true - } - } else if (typeof marker.getPosition === 'function') { - if (map.getBounds().contains(marker.getPosition())) { + if (typeof marker.getLatLng === 'function') { + if (map.getBounds().contains(marker.getLatLng())) { show = true } } else if (type === 's2cell') { @@ -1723,7 +1686,6 @@ function showInBoundsMarkers(markers, type) { } } } - // Marker has an associated range. if (show && rangeMarkers.indexOf(type) !== -1) { // No range circle yet... let's create one. @@ -1735,27 +1697,14 @@ function showInBoundsMarkers(markers, type) { } } else { // There's already a range circle. if (isRangeActive(map)) { - marker.rangeCircle.setMap(map) + markers.addLayer(marker.rangeCircle) } else { - marker.rangeCircle.setMap(null) + markers.removeLayer(marker.rangeCircle) + markersnotify.removeLayer(marker.rangeCircle) + delete marker.rangeCircle } } } - - if (show && !marker.getMap()) { - marker.setMap(map) - // Not all markers can be animated (ex: scan locations) - if (marker.setAnimation && marker.oldAnimation) { - marker.setAnimation(marker.oldAnimation) - } - } else if (!show && marker.getMap()) { - // Not all markers can be animated (ex: scan locations) - if (marker.getAnimation) { - marker.oldAnimation = marker.getAnimation() - } - if (marker.rangeCircle) marker.rangeCircle.setMap(null) - marker.setMap(null) - } }) } @@ -1775,10 +1724,10 @@ function loadRawData() { var bounds = map.getBounds() var swPoint = bounds.getSouthWest() var nePoint = bounds.getNorthEast() - var swLat = swPoint.lat() - var swLng = swPoint.lng() - var neLat = nePoint.lat() - var neLng = nePoint.lng() + var swLat = swPoint.lat + var swLng = swPoint.lng + var neLat = nePoint.lat + var neLng = nePoint.lng return $.ajax({ url: 'raw_data', @@ -1857,7 +1806,6 @@ function processPokemons(pokemon) { if (!Store.get('showPokemon')) { return false // In case the checkbox was unchecked in the meantime. } - // Process Pokémon per chunk of total so we don't overwhelm the client and // allow redraws in between. We enable redraw in addMarkers, which doesn't // repaint/reset all previous markers but only draws new ones. @@ -1873,7 +1821,6 @@ function processPokemonChunked(pokemon, chunkSize) { if (typeof pokemon === 'undefined' || pokemon.length === 0) { return } - const oldMarkers = [] const newMarkers = [] const chunk = pokemon.splice(-1 * chunkSize) @@ -1886,37 +1833,25 @@ function processPokemonChunked(pokemon, chunkSize) { if (mapData.pokemons.hasOwnProperty(encounterId) || expiringSoon) { return } + const _markers = processPokemon(poke) + const newMarker = _markers[0] + const oldMarker = _markers[1] + const isNotifyPkmn = isNotifyPoke(poke) - const markers = processPokemon(poke) - const newMarker = markers[0] - const oldMarker = markers[1] - - // Don't add Pokémon marker to clusters if we're sending a notification. - if (!isNotifyPoke(poke)) { - if (newMarker) { - newMarkers.push(newMarker) - } - - if (oldMarker) { - oldMarkers.push(oldMarker) - } - } else { - if (newMarker) { - newMarker.setMap(map) + if (newMarker) { + if (isNotifyPkmn) { + markersnotify.addLayer(newMarker) + } else { + markers.addLayer(newMarker) } + } - if (oldMarker) { - oldMarker.setMap(null) - } + if (oldMarker) { + markers.removeLayer(oldMarker) + markersnotify.removeLayer(oldMarker) } }) - // Disable instant redraw, we'll repaint ourselves after we've added the - // new markers. - markerCluster.removeMarkers(oldMarkers, true) - markerCluster.addMarkers(newMarkers, false) - - // Any left? if (pokemon.length > 0) { setTimeout(function () { processPokemonChunked(pokemon, chunkSize) @@ -1935,11 +1870,9 @@ function processPokemon(item) { const isRarityExcluded = (excludedRarity.indexOf(pokemonRarity) !== -1) const isPokeExcludedByRarity = excludedPokemonByRarity.indexOf(item['pokemon_id']) !== -1 const isNotifyPkmn = isNotifyPoke(item) - var prionotifyactiv = Store.get('prioNotify') var oldMarker = null var newMarker = null - if ((!(item['encounter_id'] in mapData.pokemons) && !isPokeExcluded && !isRarityExcluded && isPokeAlive) || (!(item['encounter_id'] in mapData.pokemons) && isNotifyPkmn && prionotifyactiv)) { // Add marker to map and item to dict. @@ -1947,14 +1880,13 @@ function processPokemon(item) { if (!item.hidden && (!Store.get('hideNotNotified') || isNotifyPkmn)) { const isBounceDisabled = Store.get('isBounceDisabled') const scaleByRarity = Store.get('scaleByRarity') - if (item.marker) { - updatePokemonMarker(item.marker, map, scaleByRarity, isNotifyPkmn) - } else { - newMarker = setupPokemonMarker(item, map, isBounceDisabled, scaleByRarity, isNotifyPkmn) - customizePokemonMarker(newMarker, item, !Store.get('showPopups')) - item.marker = newMarker + markers.removeLayer(item) + markersnotify.removeLayer(item) } + newMarker = setupPokemonMarker(item, map, isBounceDisabled, scaleByRarity, isNotifyPkmn) + customizePokemonMarker(newMarker, item, !Store.get('showPopups')) + item.marker = newMarker mapData.pokemons[item['encounter_id']] = item } else { @@ -1978,10 +1910,10 @@ function processPokestop(i, item) { if (!mapData.pokestops[item['pokestop_id']]) { // new pokestop, add marker to map and item to dict if (item.marker && item.marker.rangeCircle) { - item.marker.rangeCircle.setMap(null) + markers.removeLayer(item.marker.rangeCircle) } if (item.marker) { - item.marker.setMap(null) + markers.removeLayer(item.marker) } item.marker = setupPokestopMarker(item) mapData.pokestops[item['pokestop_id']] = item @@ -1989,9 +1921,9 @@ function processPokestop(i, item) { var item2 = mapData.pokestops[item['pokestop_id']] if (!!item['lure_expiration'] !== !!item2['lure_expiration']) { if (item2.marker && item2.marker.rangeCircle) { - item2.marker.rangeCircle.setMap(null) + markers.removeLayer(item2.marker.rangeCircle) } - item2.marker.setMap(null) + markers.removeLayer(item2.marker) item.marker = setupPokestopMarker(item) mapData.pokestops[item['pokestop_id']] = item } @@ -2011,9 +1943,9 @@ function updatePokestops() { if (value['lure_expiration'] && value['lure_expiration'] < currentTime) { value['lure_expiration'] = null if (value.marker && value.marker.rangeCircle) { - value.marker.rangeCircle.setMap(null) + markers.removeLayer(value.marker.rangeCircle) } - value.marker.setMap(null) + markers.removeLayer(value.marker) value.marker = setupPokestopMarker(value) } }) @@ -2028,10 +1960,10 @@ function updatePokestops() { $.each(removeStops, function (key, value) { if (mapData.pokestops[value] && mapData.pokestops[value].marker) { if (mapData.pokestops[value].marker.rangeCircle) { - mapData.pokestops[value].marker.rangeCircle.setMap(null) + markers.removeLayer(mapData.pokestops[value].marker.rangeCircle) } - mapData.pokestops[value].marker.setMap(null) - delete mapData.pokestops[value] + markers.removeLayer(mapData.pokestops[value].marker) + delete mapData.pokestops[key] } }) } @@ -2048,9 +1980,9 @@ function processGym(i, item) { var removeGymFromMap = function (gymid) { if (mapData.gyms[gymid] && mapData.gyms[gymid].marker) { if (mapData.gyms[gymid].marker.rangeCircle) { - mapData.gyms[gymid].marker.rangeCircle.setMap(null) + markers.removeLayer(mapData.gyms[gymid].marker.rangeCircle) } - mapData.gyms[gymid].marker.setMap(null) + markers.removeLayer(mapData.gyms[gymid].marker) delete mapData.gyms[gymid] } } @@ -2121,7 +2053,7 @@ function processScanned(i, item) { if (!(scanId in mapData.scanned)) { // add marker to map and item to dict if (item.marker) { - item.marker.setMap(null) + markersnotify.removeLayer(item.marker) } item.marker = setupScannedMarker(item) mapData.scanned[scanId] = item @@ -2136,10 +2068,9 @@ function updateScanned() { } $.each(mapData.scanned, function (key, value) { - if (map.getBounds().intersects(value.marker.getBounds())) { - value.marker.setOptions({ - fillColor: getColorByDate(value['last_modified']) - }) + if (map.getBounds().contains(value.marker.getLatLng())) { + var color = getColorByDate(value['last_modified']) + value.marker.setStyle({color: color, fillColor: color}) } }) } @@ -2153,7 +2084,7 @@ function processSpawnpoint(i, item) { if (!(id in mapData.spawnpoints)) { // add marker to map and item to dict if (item.marker) { - item.marker.setMap(null) + markersnotify.removeLayer(item.marker) } item.marker = setupSpawnpointMarker(item) mapData.spawnpoints[id] = item @@ -2165,13 +2096,10 @@ function updateSpawnPoints() { return false } - var zoom = map.getZoom() - $.each(mapData.spawnpoints, function (key, value) { - if (map.getBounds().contains(value.marker.getPosition())) { + if (map.getBounds().contains(value.marker.getLatLng())) { var hue = getColorBySpawnTime(value['appear_time']) - value.marker.setIcon(changeSpawnIcon(hue, zoom)) - value.marker.setZIndex(spawnPointIndex(hue)) + value.marker.setStyle({color: hue, fillColor: hue}) } }) } @@ -2187,7 +2115,6 @@ function updateMap() { $.each(result.s2cells, processS2Cell) processWeatherAlerts(result.weatherAlerts) updateMainCellWeather() - // showInBoundsMarkers(mapData.pokemons, 'pokemon') showInBoundsMarkers(mapData.lurePokemons, 'pokemon') showInBoundsMarkers(mapData.gyms, 'gym') showInBoundsMarkers(mapData.pokestops, 'pokestop') @@ -2199,7 +2126,7 @@ function updateMap() { clearStaleMarkers() // We're done processing. Redraw. - markerCluster.redraw() + markers.refreshClusters() updateScanned() updateSpawnPoints() @@ -2238,7 +2165,6 @@ function redrawPokemon(pokemonList) { if (!item.hidden) { const scaleByRarity = Store.get('scaleByRarity') const isNotifyPkmn = isNotifyPoke(item) - updatePokemonMarker(item, map, scaleByRarity, isNotifyPkmn) } }) @@ -2263,8 +2189,23 @@ var updateLabelDiffTime = function () { }) } -function getPointDistance(pointA, pointB) { - return google.maps.geometry.spherical.computeDistanceBetween(pointA, pointB) +function getPointDistance(origin, destination) { + // return distance in meters + var lon1 = toRadian(origin.lng) + var lat1 = toRadian(origin.lat) + var lon2 = toRadian(destination.lng) + var lat2 = toRadian(destination.lat) + var deltaLat = lat2 - lat1 + var deltaLon = lon2 - lon1 + + var a = Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin(deltaLon / 2), 2) + var c = 2 * Math.asin(Math.sqrt(a)) + var EARTH_RADIUS = 6371 + return c * EARTH_RADIUS * 1000 +} + +function toRadian(degree) { + return degree * Math.PI / 180 } function sendNotification(title, text, icon, lat, lon) { @@ -2285,7 +2226,7 @@ function sendNotification(title, text, icon, lat, lon) { if (Push._agents.desktop.isSupported()) { window.focus() event.currentTarget.close() - centerMap(lat, lon, 20) + map.setView(new L.LatLng(lat, lon), 20) } } } @@ -2305,7 +2246,7 @@ function sendToastrPokemonNotification(title, text, icon, lat, lon) { positionClass: 'toast-top-right', preventDuplicates: true, onclick: function () { - centerMap(lat, lon, 20) + map.setView(new L.LatLng(lat, lon), 20) }, showDuration: '300', hideDuration: '500', @@ -2326,41 +2267,45 @@ function sendToastrPokemonNotification(title, text, icon, lat, lon) { } function createMyLocationButton() { - var locationContainer = document.createElement('div') - - var locationButton = document.createElement('button') - locationButton.style.backgroundColor = '#fff' - locationButton.style.border = 'none' - locationButton.style.outline = 'none' - locationButton.style.width = '28px' - locationButton.style.height = '28px' - locationButton.style.borderRadius = '2px' - locationButton.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)' - locationButton.style.cursor = 'pointer' - locationButton.style.marginRight = '10px' - locationButton.style.padding = '0px' - locationButton.title = 'My Location' - locationContainer.appendChild(locationButton) - - var locationIcon = document.createElement('div') - locationIcon.style.margin = '5px' - locationIcon.style.width = '18px' - locationIcon.style.height = '18px' - locationIcon.style.backgroundImage = 'url(static/mylocation-sprite-1x.png)' - locationIcon.style.backgroundSize = '180px 18px' - locationIcon.style.backgroundPosition = '0px 0px' - locationIcon.style.backgroundRepeat = 'no-repeat' - locationIcon.id = 'current-location' - locationButton.appendChild(locationIcon) - - locationButton.addEventListener('click', function () { - centerMapOnLocation() - }) + var _locationMarker = L.control({position: 'bottomright'}) + var locationContainer + + _locationMarker.onAdd = function (map) { + locationContainer = L.DomUtil.create('div', '_locationMarker') + + var locationButton = document.createElement('button') + locationButton.style.backgroundColor = '#fff' + locationButton.style.border = '2px solid rgba(0,0,0,0.2)' + locationButton.style.outline = 'none' + locationButton.style.width = '34px' + locationButton.style.height = '34px' + locationButton.style.cursor = 'pointer' + locationButton.style.padding = '0px' + locationButton.title = 'My Location' + locationContainer.appendChild(locationButton) + + var locationIcon = document.createElement('div') + locationIcon.style.margin = '5px' + locationIcon.style.width = '18px' + locationIcon.style.height = '18px' + locationIcon.style.backgroundImage = 'url(static/mylocation-sprite-1x.png)' + locationIcon.style.backgroundSize = '200px 19px' + locationIcon.style.backgroundPosition = '0px 0px' + locationIcon.style.backgroundRepeat = 'no-repeat' + locationIcon.id = 'current-location' + locationButton.appendChild(locationIcon) + + locationButton.addEventListener('click', function () { + centerMapOnLocation() + }) + return locationContainer + } + + _locationMarker.addTo(map) locationContainer.index = 1 - map.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(locationContainer) - google.maps.event.addListener(map, 'dragend', function () { + map.on('dragend', function () { var currentLocation = document.getElementById('current-location') currentLocation.style.backgroundPosition = '0px 0px' }) @@ -2370,26 +2315,25 @@ function centerMapOnLocation() { var currentLocation = document.getElementById('current-location') var imgX = '0' var animationInterval = setInterval(function () { - if (imgX === '-18') { + if (imgX === '-20') { imgX = '0' } else { - imgX = '-18' + imgX = '-20' } currentLocation.style.backgroundPosition = imgX + 'px 0' }, 500) if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function (position) { - var latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude) + var latlng = new L.LatLng(position.coords.latitude, position.coords.longitude) if (locationMarker) { - locationMarker.setPosition(latlng) + locationMarker.setLatLng(latlng) } - map.setCenter(latlng) + map.panTo(latlng) Store.set('followMyLocationPosition', { lat: position.coords.latitude, lng: position.coords.longitude }) clearInterval(animationInterval) - currentLocation.style.backgroundPosition = '-144px 0px' }) } else { clearInterval(animationInterval) @@ -2398,11 +2342,11 @@ function centerMapOnLocation() { } function changeLocation(lat, lng) { - var loc = new google.maps.LatLng(lat, lng) + var loc = new L.LatLng(lat, lng) changeSearchLocation(lat, lng).done(function () { - map.setCenter(loc) + map.panTo(loc) if (searchMarker) { - searchMarker.setPosition(loc) + searchMarker.setLatLng(loc) } }) } @@ -2412,9 +2356,9 @@ function changeSearchLocation(lat, lng) { } function centerMap(lat, lng, zoom) { - var loc = new google.maps.LatLng(lat, lng) + var loc = new L.LatLng(lat, lng) - map.setCenter(loc) + map.panTo(loc) if (zoom) { storeZoom = false @@ -2450,21 +2394,21 @@ function updateGeoLocation() { navigator.geolocation.getCurrentPosition(function (position) { var lat = position.coords.latitude var lng = position.coords.longitude - var center = new google.maps.LatLng(lat, lng) + var center = new L.LatLng(lat, lng) if (Store.get('geoLocate')) { // The search function makes any small movements cause a loop. Need to increase resolution. - if ((typeof searchMarker !== 'undefined') && (getPointDistance(searchMarker.getPosition(), center) > 40)) { + if ((typeof searchMarker !== 'undefined') && (getPointDistance(searchMarker.getLatLng(), center) > 40)) { $.post('next_loc?lat=' + lat + '&lon=' + lng).done(function () { map.panTo(center) - searchMarker.setPosition(center) + searchMarker.setLatLng(center) }) } } if (Store.get('followMyLocation')) { - if ((typeof locationMarker !== 'undefined') && (getPointDistance(locationMarker.getPosition(), center) >= 5)) { + if ((typeof locationMarker !== 'undefined') && (getPointDistance(locationMarker.getLatLng(), center) >= 5)) { map.panTo(center) - locationMarker.setPosition(center) + locationMarker.setLatLng(center) Store.set('followMyLocationPosition', { lat: lat, lng: lng @@ -2545,7 +2489,6 @@ function showGymDetails(id) { // eslint-disable-line no-unused-vars Gym Leader:
${pokemonIcon}
${result.guard_pokemon_name} -

No additional gym information is available for this gym. Make sure you are collecting detailed gym info. If you have detailed gym info collection running, this gym's Pokemon information may be out of date. @@ -2752,7 +2695,7 @@ $(function () { // setup the list change behavior $selectStyle.on('change', function (e) { selectedStyle = $selectStyle.val() - map.setMapTypeId(selectedStyle) + setTitleLayer(selectedStyle) Store.set('map_style', selectedStyle) }) @@ -2786,7 +2729,7 @@ $(function () { redrawPokemon(mapData.lurePokemons) // We're done processing the list. Repaint. - markerCluster.repaint() + markers.refreshClusters() }) $switchOpenGymsOnly = $('#open-gyms-only-switch') @@ -2905,10 +2848,10 @@ $(function () { $.each(mapData[dType], function (key, value) { // for any marker you're turning off, you'll want to wipe off the range if (mapData[dType][key].marker.rangeCircle) { - mapData[dType][key].marker.rangeCircle.setMap(null) + markers.removeLayer(mapData[dType][key].marker.rangeCircle) delete mapData[dType][key].marker.rangeCircle } - mapData[dType][key].marker.setMap(null) + markers.removeLayer(mapData[dType][key].marker) }) mapData[dType] = {} }) @@ -2950,7 +2893,7 @@ $(function () { $selectSearchIconMarker.on('change', function (e) { var selectSearchIconMarker = $selectSearchIconMarker.val() Store.set('searchMarkerStyle', selectSearchIconMarker) - updateSearchMarker(selectSearchIconMarker) + setTimeout(function () { updateSearchMarker(selectSearchIconMarker) }, 300) }) $selectSearchIconMarker.val(Store.get('searchMarkerStyle')).trigger('change') @@ -2964,14 +2907,15 @@ $(function () { }) $selectLocationIconMarker.on('change', function (e) { - Store.set('locationMarkerStyle', this.value) - updateLocationMarker(this.value) + var locStyle = this.value + Store.set('locationMarkerStyle', locStyle) + setTimeout(function () { updateLocationMarker(locStyle) }, 300) }) $selectLocationIconMarker.val(Store.get('locationMarkerStyle')).trigger('change') + loadDefaultImages() }) }) - $(function () { moment.locale(language) function formatState(state) { @@ -2990,10 +2934,48 @@ $(function () { return $state } + function formatRarityState(state) { + if (!state.id) { + return state.text + } + var pokemonId + switch (state.element.value.toString()) { + case i8ln('Common'): + pokemonId = Store.get('rarityCommon') + break + case i8ln('Uncommon'): + pokemonId = Store.get('rarityUncommon') + break + case i8ln('Rare'): + pokemonId = Store.get('rarityRare') + break + case i8ln('Very Rare'): + pokemonId = Store.get('rarityVeryRare') + break + case i8ln('Ultra Rare'): + pokemonId = Store.get('rarityUltraRare') + break + case i8ln('New Spawn'): + pokemonId = Store.get('rarityNewSpawn') + break + default: + pokemonId = 1 + } + var pokemonIcon + if (generateImages) { + pokemonIcon = `` + } else { + pokemonIcon = `` + } + var $state = $( + `${pokemonIcon} ${state.text}` + ) + return $state + } + if (Store.get('startAtUserLocation') && getParameterByName('lat') == null && getParameterByName('lon') == null) { centerMapOnLocation() } - $.getJSON('static/dist/data/moves.min.json').done(function (data) { moves = data }) @@ -3004,59 +2986,166 @@ $(function () { $selectRarityNotify = $('#notify-rarity') $textPerfectionNotify = $('#notify-perfection') $textLevelNotify = $('#notify-level') - var numberOfPokemon = 493 + var numberOfPokemon = 384 + $('.list').before('') + const hidepresets = Store.get('hidepresets') + + $.each(hidepresets, function (key, value) { + var pokemonIcon + var iconid = value['PokemonID'] + if (generateImages) { + pokemonIcon = `` + } else { + pokemonIcon = `` + } + $('.exclude_templates').append('

' + pokemonIcon + '
' + value['Name'] + '
') + }) // Load pokemon names and populate lists $.getJSON('static/dist/data/pokemon.min.json').done(function (data) { + var pokemonIcon + var typestring = [] var pokeList = [] - $.each(data, function (key, value) { if (key > numberOfPokemon) { return false } - var _types = [] - pokeList.push({ - id: key, - text: i8ln(value['name']) + ' - #' + key - }) + if (generateImages) { + pokemonIcon = `` + } else { + pokemonIcon = `` + } value['name'] = i8ln(value['name']) - value['rarity'] = i8ln(value['rarity']) $.each(value['types'], function (key, pokemonType) { - _types.push({ - 'type': i8ln(pokemonType['type']), - 'color': pokemonType['color'] - }) + typestring[key] = i8ln(pokemonType['type']) }) - value['types'] = _types + value['gen'] = getPokemonGen(key) + $('.list').append('
#' + key + '
' + pokemonIcon + '
' + i8ln(value['name']) + '
') idToPokemon[key] = value + pokeSearchList.push({ + value: key, + pkm: i8ln(value['name']), + gen: 'gen' + value['gen'], + type1: typestring[0], + type2: typestring[1], + allpokemon: 'allpokemon' + }) }) - // setup the filter lists - $selectExclude.select2({ - placeholder: i8ln('Select Pokémon'), - data: pokeList, - templateResult: formatState - }) - $selectPokemonNotify.select2({ - placeholder: i8ln('Select Pokémon'), - data: pokeList, - templateResult: formatState - }) $selectRarityNotify.select2({ placeholder: i8ln('Select Rarity'), data: [i8ln('Common'), i8ln('Uncommon'), i8ln('Rare'), i8ln('Very Rare'), i8ln('Ultra Rare'), i8ln('New Spawn')], - templateResult: formatState + templateResult: formatRarityState }) - // setup list change behavior now that we have the list to work from + $('.list').on('click', '.pokemon-icon-sprite', function () { + var img = $(this) + var select = $(this).parent().parent().find('input[id$=pokemon]') + var value = select.val().split(',') + $(this).find('.hidepreset').removeClass('active') + var id = img.data('value').toString() + $('.hidepreset').removeClass('active') + if (img.hasClass('active')) { + select.val(value.filter(function (elem) { + return elem !== id + }).join(',')).trigger('change') + img.removeClass('active') + } else { + select.val((value.concat(id).join(','))).trigger('change') + img.addClass('active') + } + }) + + $('.exclude_templates').on('click', '.hidepreset', function () { + const hidepresets = Store.get('hidepresets') + var img = $(this) + var id = img.data('key').toString() + $('.hidepreset').removeClass('active') + img.addClass('active') + generatePokemonExclude(i8ln(hidepresets[id]['Searchstring']), hidepresets[id]['Invert']) + }) + + $('.search').on('input', function () { + var searchtext = $(this).val().toString() + var parent = $(this) + var foundpokemon = [] + var pokeselectlist = $(this).next('.list').find('.pokemon-icon-sprite') + if (searchtext === '') { + parent.parent().find('.select-all, .select-reverse').hide() + parent.parent().find('.hide-all').show() + pokeselectlist.show() + } else { + pokeselectlist.hide() + parent.parent().find('.select-all, .select-reverse').show() + parent.parent().find('.hide-all').hide() + foundpokemon = filterpokemon(pokeSearchList, searchtext.replace(/\s/g, '')) + } + + $.each(foundpokemon, function (i, item) { + parent.next('.list').find('.pokemon-icon-sprite[data-value="' + foundpokemon[i] + '"]').show() + }) + foundpokemon = [] + }) + + loadDefaultImages() + + $('.select-reverse').on('click', function (e) { + e.preventDefault() + var selectlist = [] + var parent = $(this).parent().parent() + var pokeselectlist = parent.find('.pokemon-icon-sprite') + pokeselectlist.removeClass('active') + pokeselectlist = parent.find('.pokemon-icon-sprite:hidden') + pokeselectlist.addClass('active') + $('.hidepreset').removeClass('active') + + $.each(pokeselectlist, function (i, item) { + var pokemonicon = $(this) + selectlist.push(pokemonicon.data('value')) + }) + parent.find('input[id$=pokemon]').val(selectlist.join(',')).trigger('change') + }) + + $('.select-all').on('click', function (e) { + e.preventDefault() + var selectlist = [] + var parent = $(this).parent().parent() + var pokeselectlist = parent.find('.pokemon-icon-sprite') + pokeselectlist.removeClass('active') + pokeselectlist = parent.find('.pokemon-icon-sprite:visible') + pokeselectlist.addClass('active') + $('.hidepreset').removeClass('active') + + $.each(pokeselectlist, function (i, item) { + var pokemonicon = $(this) + selectlist.push(pokemonicon.data('value')) + }) + parent.find('input[id$=pokemon]').val(selectlist.join(',')).trigger('change') + }) + $('.hide-all').on('click', function (e) { + e.preventDefault() + var parent = $(this).parent().parent() + $('.hidepreset').removeClass('active') + parent.find('.list .pokemon-icon-sprite:visible').removeClass('active') + parent.find('input[id$=pokemon]').val('').trigger('change') + }) $selectExclude.on('change', function (e) { buffer = excludedPokemon - excludedPokemon = $selectExclude.val().map(Number) + excludedPokemon = $selectExclude.val().split(',').map(Number).sort(function (a, b) { + return parseInt(a) - parseInt(b) + }) buffer = buffer.filter(function (e) { return this.indexOf(e) < 0 }, excludedPokemon) - reincludedPokemon = reincludedPokemon.concat(buffer) + reincludedPokemon = reincludedPokemon.concat(buffer).map(String) clearStaleMarkers() + if (excludedPokemon.length === 1) { + $('.hidefilteractiv').text('*** No active Filter ***') + $('.hidefilteractiv').css('color', 'black') + } else { + $('.hidefilteractiv').text('*** active Filter ***') + $('.hidefilteractiv').css('color', 'red') + } Store.set('remember_select_exclude', excludedPokemon) }) $selectExcludeRarity.on('change', function (e) { @@ -3067,7 +3156,22 @@ $(function () { Store.set('excludedRarity', excludedRarity) }) $selectPokemonNotify.on('change', function (e) { - notifiedPokemon = $selectPokemonNotify.val().map(Number) + buffer = notifiedPokemon + notifiedPokemon = $selectPokemonNotify.val().split(',').map(Number).sort(function (a, b) { + return parseInt(a) - parseInt(b) + }) + buffer = buffer.filter(function (e) { + return this.indexOf(e) < 0 + }, notifiedPokemon) + reincludedPokemon = reincludedPokemon.concat(buffer).map(String) + clearStaleMarkers() + if (notifiedPokemon.length === 1) { + $('.notifyfilteractiv').text('*** No active Filter ***') + $('.notifyfilteractiv').css('color', 'black') + } else { + $('.notifyfilteractiv').text('*** active Filter ***') + $('.notifyfilteractiv').css('color', 'red') + } Store.set('remember_select_notify', notifiedPokemon) }) $selectRarityNotify.on('change', function (e) { @@ -3112,12 +3216,49 @@ $(function () { // run interval timers to regularly update map, rarity and timediffs window.setInterval(updateLabelDiffTime, 1000) - window.setInterval(updateMap, 5000) + window.setInterval(updateMap, 2000) window.setInterval(updatePokemonRarities, 300000) window.setInterval(updateGeoLocation, 1000) createUpdateWorker() + function isNumber(n) { return !isNaN(parseFloat(n)) && !isNaN(n - 0) } + + function filterpokemon(pokemonarray, searchtext) { + if (searchtext.substring(0, 1) === '-') { searchtext = 'allpokemon,' + searchtext } + var searchsplit = searchtext.split(',') + var foundpokemon = [] + var operator = 'add' + $.each(searchsplit, function (k, searchstring) { + if (searchstring.substring(0, 1) === '+') { + searchstring = searchstring.substring(1) + operator = 'add' + } else if (searchstring.substring(0, 1) === '-') { + searchstring = searchstring.substring(1) + operator = 'remove' + } else { + operator = 'add' + } + if (isNumber(searchstring)) { + if (operator === 'add') { + foundpokemon.push(searchstring) + } else { + delete foundpokemon[foundpokemon.indexOf(searchstring)] + } + } else if (searchstring.length > 0 && searchstring !== '-' && searchstring !== '+') { + $.each(pokemonarray, function (i, item) { + if ((item['pkm'].toLowerCase().indexOf(searchstring.toLowerCase()) !== -1) || (i8ln(item['type1'].toLowerCase()).indexOf(i8ln(searchstring).toLowerCase()) !== -1) || (i8ln(item['type2'].toLowerCase()).indexOf(i8ln(searchstring).toLowerCase()) !== -1) || (item['gen'].toString() === searchstring.toLowerCase()) || (item['value'].toString() === searchstring.toString()) || (item['allpokemon'].toString() === searchstring.toString())) { + if (operator === 'add') { + foundpokemon.push(item['value']) + } else { + delete foundpokemon[foundpokemon.indexOf(item['value'])] + } + } + }) + } + }) + return foundpokemon + } // Wipe off/restore map icons when switches are toggled function buildSwitchChangeListener(data, dataType, storageKey) { return function () { @@ -3146,10 +3287,13 @@ $(function () { $.each(data[dType], function (key, value) { // for any marker you're turning off, you'll want to wipe off the range if (data[dType][key].marker.rangeCircle) { - data[dType][key].marker.rangeCircle.setMap(null) + markers.removeLayer(data[dType][key].marker.rangeCircle) + markersnotify.removeLayer(data[dType][key].marker.rangeCircle) delete data[dType][key].marker.rangeCircle } - data[dType][key].marker.setMap(null) + markers.removeLayer(data[dType][key].marker) + markersnotify.removeLayer(data[dType][key].marker) + delete data[dType][key].marker }) data[dType] = {} }) @@ -3160,11 +3304,13 @@ $(function () { $.each(data[dType], function (key, value) { // for any marker you're turning off, you'll want to wipe off the range if (data[dType][key].marker.rangeCircle) { - data[dType][key].marker.rangeCircle.setMap(null) + markers.removeLayer(data[dType][key].marker.rangeCircle) + markersnotify.removeLayer(data[dType][key].marker.rangeCircle) delete data[dType][key].marker.rangeCircle } if (storageKey !== 'showRanges') { - data[dType][key].marker.setMap(null) + markers.removeLayer(data[dType][key].marker) + markersnotify.removeLayer(data[dType][key].marker) if (dType === 'pokemons') { oldPokeMarkers.push(data[dType][key].marker) } @@ -3172,7 +3318,8 @@ $(function () { }) // If the type was "pokemons". if (oldPokeMarkers.length > 0) { - markerCluster.removeMarkers(oldPokeMarkers) + markers.removeLayer(oldPokeMarkers) + markersnotify.removeLayer(oldPokeMarkers) } if (storageKey !== 'showRanges') data[dType] = {} }) @@ -3183,6 +3330,34 @@ $(function () { } } + function diffPokemon(array1, array2) { + var temp = [] + array1 = array1.toString().split(',').map(Number) + array2 = array2.toString().split(',').map(Number) + for (var i in array1) { + if (array2.indexOf(array1[i]) === -1) temp.push(array1[i]) + } + for (i in array2) { + if (array1.indexOf(array2[i]) === -1) temp.push(array2[i]) + } + return temp.sort((a, b) => a - b) + } + + function generatePokemonExclude(value, invert) { + var allpokemon = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386] + value = value.toString() + allpokemon = allpokemon.map(String) + if (invert === false) { + $selectExclude.val(filterpokemon(pokeSearchList, value.replace(/\s/g, ''))).trigger('change') + } else { + $selectExclude.val(diffPokemon(allpokemon, filterpokemon(pokeSearchList, value.replace(/\s/g, '')).map(String))).trigger('change') + } + loadDefaultImages() + redrawPokemon(mapData.pokemons) + redrawPokemon(mapData.lurePokemons) + } + + function resetGymFilter() { Store.set('showTeamGymsOnly', 0) Store.set('minGymLevel', 0) @@ -3200,6 +3375,8 @@ $(function () { } // Setup UI element interactions + + $('#gyms-switch').change(function () { var options = { 'duration': 500 @@ -3243,7 +3420,7 @@ $(function () { }) $('#pokemon-switch').change(function () { buildSwitchChangeListener(mapData, ['pokemons'], 'showPokemon').bind(this)() - markerCluster.repaint() + markers.refreshClusters() }) $('#pokemon-stats-switch').change(function () { Store.set('showPokemonStats', this.checked) @@ -3360,7 +3537,7 @@ $(function () { $('#lock-marker-switch').change(function () { Store.set('lockMarker', this.checked) if (searchMarker) { - searchMarker.setDraggable(!this.checked) + searchMarker.draggable = (!this.checked) } }) @@ -3381,11 +3558,11 @@ $(function () { if (locationMarker) { if (this.checked) { // Follow our position programatically, so no dragging. - locationMarker.setDraggable(false) + locationMarker.draggable = false } else { // Go back to default non-follow. const isMarkerMovable = Store.get('isLocationMarkerMovable') - locationMarker.setDraggable(isMarkerMovable) + locationMarker.draggable = isMarkerMovable } } }) diff --git a/static/js/statistics.js b/static/js/statistics.js index 159fea527..9e509b6a5 100644 --- a/static/js/statistics.js +++ b/static/js/statistics.js @@ -1,6 +1,8 @@ -/*global getPokemonRawIconUrl*/ +/*global getPokemonRawIconUrl, L*/ /* Main stats page */ var rawDataIsLoading = false +var mapstat +var markers function loadRawData() { return $.ajax({ @@ -238,16 +240,16 @@ function closeTimes() { } function addListeners(marker) { // eslint-disable-line no-unused-vars - marker.addListener('click', function () { + marker.on('click', function () { showTimes(marker) detailsPersist = true }) - marker.addListener('mouseover', function () { + marker.on('mouseover', function () { showTimes(marker) }) - marker.addListener('mouseout', function () { + marker.on('mouseout', function () { if (!detailsPersist) { $('#times_list').hide() } @@ -255,120 +257,42 @@ function addListeners(marker) { // eslint-disable-line no-unused-vars return marker } - // Override map.js initMap -function initMap() { - map = new google.maps.Map(document.getElementById('location_map'), { +function initStat() { + mapstat = L.map('location_map', { + center: [centerLat, centerLng], zoom: 16, - center: { - lat: centerLat, - lng: centerLng - }, - fullscreenControl: false, - streetViewControl: false, - mapTypeControl: true, - clickableIcons: false, - mapTypeControlOptions: { - style: google.maps.MapTypeControlStyle.DROPDOWN_MENU, - position: google.maps.ControlPosition.RIGHT_TOP, - mapTypeIds: [ - google.maps.MapTypeId.ROADMAP, - google.maps.MapTypeId.SATELLITE, - google.maps.MapTypeId.HYBRID, - 'nolabels_style', - 'dark_style', - 'style_light2', - 'style_pgo', - 'dark_style_nl', - 'style_light2_nl', - 'style_pgo_nl', - 'style_pgo_day', - 'style_pgo_night', - 'style_pgo_dynamic' - ] - } - }) - - var styleNoLabels = new google.maps.StyledMapType(noLabelsStyle, { - name: 'No Labels' - }) - map.mapTypes.set('nolabels_style', styleNoLabels) - - var styleDark = new google.maps.StyledMapType(darkStyle, { - name: 'Dark' - }) - map.mapTypes.set('dark_style', styleDark) - - var styleLight2 = new google.maps.StyledMapType(light2Style, { - name: 'Light2' - }) - map.mapTypes.set('style_light2', styleLight2) - - var stylePgo = new google.maps.StyledMapType(pGoStyle, { - name: 'RocketMap' - }) - map.mapTypes.set('style_pgo', stylePgo) - - var styleDarkNl = new google.maps.StyledMapType(darkStyleNoLabels, { - name: 'Dark (No Labels)' - }) - map.mapTypes.set('dark_style_nl', styleDarkNl) - - var styleLight2Nl = new google.maps.StyledMapType(light2StyleNoLabels, { - name: 'Light2 (No Labels)' + zoomControl: true, + maxZoom: 18 }) - map.mapTypes.set('style_light2_nl', styleLight2Nl) - var stylePgoNl = new google.maps.StyledMapType(pGoStyleNoLabels, { - name: 'RocketMap (No Labels)' - }) - map.mapTypes.set('style_pgo_nl', stylePgoNl) - - var stylePgoDay = new google.maps.StyledMapType(pGoStyleDay, { - name: 'RocketMap Day' - }) - map.mapTypes.set('style_pgo_day', stylePgoDay) - - var stylePgoNight = new google.maps.StyledMapType(pGoStyleNight, { - name: 'RocketMap Night' - }) - map.mapTypes.set('style_pgo_night', stylePgoNight) - - // dynamic map style chooses stylePgoDay or stylePgoNight depending on client time - var currentDate = new Date() - var currentHour = currentDate.getHours() - var stylePgoDynamic = (currentHour >= 6 && currentHour < 19) ? stylePgoDay : stylePgoNight - map.mapTypes.set('style_pgo_dynamic', stylePgoDynamic) - - map.addListener('maptypeid_changed', function (s) { - Store.set('map_style', this.mapTypeId) - }) - - map.setMapTypeId(Store.get('map_style')) + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap' + }).addTo(mapstat) + markers = L.layerGroup().addTo(mapstat) mapLoaded = true - - google.maps.event.addListener(map, 'zoom_changed', function () { + mapstat.on('zoom', function () { redrawAppearances(mapData.appearances) }) } function resetMap() { $.each(mapData.appearances, function (key, value) { - mapData.appearances[key].marker.setMap(null) + markers.clearLayers() delete mapData.appearances[key] }) heatmapPoints = [] if (heatmap) { - heatmap.setMap(null) + // heatmapstat.setMap(null) } } function showOverlay(id) { // Only load google maps once, and only if requested if (!mapLoaded) { - initMap() + initStat() } resetMap() pokemonid = id @@ -376,6 +300,8 @@ function showOverlay(id) { location.hash = 'overlay_' + pokemonid updateDetails() + setTimeout(function () { mapstat.invalidateSize() }, 400) + return false } @@ -397,12 +323,12 @@ function processAppearance(i, item) { item['marker'].setMap(null) } item['marker'] = setupPokemonMarker(item, map, isBounceDisabled, scaleByRarity, isNotifyPkmn) - item['marker'].setMap(map) + markers.addLayer(item['marker']) addListeners(item['marker']) item['marker'].spawnpointId = spawnpointId mapData.appearances[spawnpointId] = item } - heatmapPoints.push({location: new google.maps.LatLng(item['latitude'], item['longitude']), weight: parseFloat(item['count'])}) + heatmapPoints.push([item['latitude'], item['longitude'], parseFloat(item['count'])]) } function redrawAppearances(appearances) { @@ -413,9 +339,9 @@ function redrawAppearances(appearances) { const scaleByRarity = false // ..nor this.. const isNotifyPkmn = false // ..and especially not this. - item['marker'].setMap(null) + // item['marker'].setMap(null) const newMarker = setupPokemonMarker(item, map, isBounceDisabled, scaleByRarity, isNotifyPkmn) - newMarker.setMap(map) + markers.addLayer(newMarker) addListeners(newMarker) newMarker.spawnpointId = item['spawnpoint_id'] appearances[key].marker = newMarker @@ -455,19 +381,20 @@ function updateDetails() { loadDetails().done(function (result) { $.each(result.appearances, processAppearance) if (heatmap) { - heatmap.setMap(null) + // heatmap.setMap(null) } - heatmap = new google.maps.visualization.HeatmapLayer({ - data: heatmapPoints, - map: map, - radius: 50 - }) + setTimeout(function () { addHeadmap(heatmapPoints) }, 1000) }).fail(function () { // Wait for next retry. setTimeout(updateDetails, 1000) }) } +function addHeadmap(headmapdata) { + heatmap = new L.HeatLayer(headmapdata, {radius: 50}).addTo(markers) + return false +} + if (location.href.match(/overlay_[0-9]+/g)) { showOverlay(location.href.replace(/^.*overlay_([0-9]+).*$/, '$1')) } diff --git a/static/js/vendor/bouncemarker.js b/static/js/vendor/bouncemarker.js new file mode 100644 index 000000000..e416e8870 --- /dev/null +++ b/static/js/vendor/bouncemarker.js @@ -0,0 +1,1029 @@ +/* + * Copyright (c) 2015, Alexei KLENIN + * 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. + */ + +/** + * Plugin for smooth bouncing of Leaflet markers. + * + * @author Alexei KLENIN + */ +;(function(L) { + + 'use strict'; + + var regStyle = /([\w-]+): ([^;]+);/g, // regex to parse style definitions + + /** + * CSS3 transform properties for different browsers + */ + css3_transforms = { + transform : 'transform', + WebkitTransform : '-webkit-transform', + OTransform : '-o-transform', + MozTransform : '-moz-transform', + msTransform : '-ms-transform' + }, + + /** + * CSS3 transform property for this browser + */ + transform = css3_transforms[L.DomUtil.TRANSFORM], + + /* Cache for motion data that not depends on x & y of the marker: + * - moveSteps + * - moveDelays + * - resizeSteps + * - resizeDelays + */ + _bouncingMotionsCache = {}; + + /* ------------------------------------------------------------------------- + * In-closure helper functions + * ------------------------------------------------------------------------- + */ + + /** + * Parses cssText attribute into object. Style definitions becomes the keys + * of the object. + * + * @param cssText cssText string + * + * @return object with style definitions as keys + */ + function parseCssText(cssText) { + var styleDefinitions = {}, + match = regStyle.exec(cssText); + + while (match) { + styleDefinitions[match[1]] = match[2]; + match = regStyle.exec(cssText); + } + + return styleDefinitions; + } + + /** + * Renders object with style definitions as string. Created string is ready + * to put in cssText attribute. + * + * @param styleDefinitions object with style definitions + * + * @return cssText string + */ + function renderCssText(styleDefinitions) { + var cssText = '', + key; + + for (key in styleDefinitions) { + cssText += key + ': ' + styleDefinitions[key] + '; ' + } + return cssText; + } + + /** + * Calculates the points to draw the continous line on the screen. Returns + * the array of ordered point coordinates. Uses Bresenham algorithm. + * + * @param x x coordinate of origin + * @param y y coordinate of origin + * @param angle angle (radians) + * @param length length of line (px) + * + * @return array of ordered point coordinates + * + * @see + * http://rosettacode.org/wiki/Bitmap/Bresenham's_line_algorithm#JavaScript + */ + function calculateLine(x, y, angle, length) { + // TODO: use something else than multiply length by 2 to calculate the + // line with defined length + var xD = Math.round(x + Math.cos(angle) * (length * 2)), + yD = Math.round(y + Math.sin(angle) * (length * 2)), + + dx = Math.abs(xD - x), + sx = x < xD ? 1 : -1, + + dy = Math.abs(yD - y), + sy = y < yD ? 1 : -1, + + err = (dx > dy ? dx : -dy) / 2, + e2, + + p = [], + i = 0; + + while (true) { + p.push([x, y]); + i++; + if (i === length) + break; + e2 = err; + if (e2 > -dx) { + err -= dy; + x += sx; + } + if (e2 < dy) { + err += dx; + y += sy; + } + } + + return p; + } + + /** + * Returns calculated array of points for icon movement. Used to animate + * markers in browsers that doesn't support 'transform' attribute. + * + * @param x x coordinate of original position of the marker + * @param y y coordinate of original position of the marker + * @param bounceHeight height of bouncing (px) + * + * @return array of points [x, y] + */ + function calculateIconMovePoints(x, y, bounceHeight) { + var p = [], // array of points + dH = bounceHeight + 1; // delta of height + + /* Use fast inverse while loop to fill the array */ + while (dH--) { + p[dH] = [x, y - dH]; + } + + return p; + } + + /** + * Returns calculated array of points for shadow movement. Used to animate + * markers in browsers that doesn't support 'transform' attribute. + * + * @param x x coordinate of original position of the marker + * @param y y coordinate of original position of the marker + * @param bounceHeight height of bouncing (px) + * @param angle shadow inclination angle, if null shadow don't + * moves from it's initial position (radians) + * + * @return array of the points [x, y] + */ + function calculateShadowMovePoints(x, y, bounceHeight, angle) { + if (angle != null) { // important: 0 is not null + return calculateLine(x, y, angle, bounceHeight + 1); + } else { + for (var p = [], i = 0; i <= bounceHeight; i++) { + p[i] = [x, y]; + } + return p; + } + } + + /** + * Returns calculated array of transformation definitions for the animation + * of icon movement. Function defines one transform for every pixel of shift + * of the icon from it's original y position. + * + * @param x x coordinate of original position of the marker + * @param y y coordinate of original position of the marker + * @param bounceHeight height of bouncing (px) + * + * @return array of transformation definitions + */ + function calculateIconMoveTransforms(x, y, bounceHeight) { + var t = [], // array of transformations + dY = bounceHeight + 1; // delta Y + + /* Use fast inverse while loop to fill the array */ + while (dY--) { + + /* Use matrix3d for hardware acceleration */ + t[dY] = ' matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,' + x + ',' + (y - dY) + + ',0,1) '; + } + + return t; + } + + /** + * Returns calculated array of transformation definitions for the animation + * of shadow movement. Function defines one transform for every pixel of + * shift of the shadow from it's original position. + * + * @param x x coordinate of original position of marker + * @param y y coordinate of original position of marker + * @param bounceHeight height of bouncing (px) + * @param angle shadow inclination angle, if null shadow don't + * moves from it's initial position (radians) + * + * @return array of transformation definitions + */ + function calculateShadowMoveTransforms(x, y, bounceHeight, angle) { + // TODO: check this method to know if bounceHeight + 1 is normal + var t = [], // array of transformation definitions + dY = bounceHeight + 1; // delta Y + + if (angle != null) { // important: 0 is not null + var p = calculateLine(x, y, angle, bounceHeight + 1); + } else { + for (var p = [], i = 0; i <= bounceHeight; i++) { + p[i] = [x, y]; + } + } + + /* Use fast inverse while loop to fill the array */ + while (dY--) { + + /* Use matrix3d for hardware acceleration */ + t[dY] = ' matrix3d(1,0,0,0,0,1,0,0,0,0,1,0,' + p[dY][0] + ',' + + p[dY][1] + ',0,1) '; + } + + return t; + } + + /** + * Returns calculated array of transformation definitions for the animation + * of icon resizing. Function defines one transform for every pixel of + * resizing of marker from it's original height. + * + * @param x x coordinate of original position of marker + * @param y y coordinate of original position of marker + * @param height original marker height (px) + * @param contractHeight height of marker contraction (px) + * + * @return array of transformation definitions + */ + function calculateIconResizeTransforms(x, y, height, contractHeight) { + var t = [], // array of transformations + dH = contractHeight + 1; // delta of height + + /* Use fast inverse while loop to fill the array */ + while (dH--) { + + /* Use matrix3d for hardware acceleration */ + t[dH] = ' matrix3d(1,0,0,0,0,' + ((height - dH) / height) + + ',0,0,0,0,1,0,' + x + ',' + (y + dH) + ',0,1) '; + } + + return t; + } + + /** + * Returns calculated array of transformation definitions for the animation + * of shadow resizing. Function defines one transform for every pixel of + * shadow resizing. + * + * @param x x coordinate of original position of marker + * @param y y coordinate of original position of marker + * @param width original marker width (px) + * @param height original marker height (px) + * @param contractHeight height of marker contraction (px) + * @param angle shadow inclination angle (radians) + * + * @return array of transformation definitions + */ + // TODO: fix & deploy this function + function calculateShadowResizeTransforms(x, y, width, height, + contractHeight, angle) { + var t = [], // array of transformation definitions + p = calculateLine(width, height, angle + Math.PI, contractHeight), + dH = contractHeight + 1; // delta height + + /* Use fast inverse while loop to fill the array */ + while (dH--) { + + /* Use matrix3d for hardware acceleration */ + t[dH] = ' matrix3d(' + (width / p[dH][0]) + ',0,0,0,0,' + + (p[dH][1] / height) + ',0,0,0,0,1,0,' + x + ',' + + (y + height - p[dH][1]) + ',0,1) '; + } + + return t; + } + + /** + * Returns calculated array of anination steps. This function used to + * calculate both movement and resizing animations. Arrays of steps are then + * cached in _bouncingMotionsCache. Function checks this cache before make + * any calculations. + * + * @param height height of movement or resizing (px) + * @param prefix prefix of the key in the cache. Must be any string with + * trailing "_" caracter. + * + * @return array of animation steps + */ + function calculateSteps(height, prefix) { + var key = prefix + height, + steps = [], + i; + + /* Check the cache */ + if (_bouncingMotionsCache[key]) { + return _bouncingMotionsCache[key]; + } + + /* Calculate the sequence of animation steps: + * steps = [1 .. height] concat [height-1 .. 0] + */ + i = 1; + while (i <= height) { + steps.push(i++); + } + + i = height; + while (i--) { + steps.push(i); + } + + /* Save steps to the cache */ + _bouncingMotionsCache[key] = steps; + + return steps; + } + + /** + * Returns calculated array of delays between animation start and the steps + * of animation. This function used to calculate both movement and resizing + * animations. Element with index i of this array contains the delay in + * milliseconds between animation start and the step number i. Those delays + * are cached in _bouncingMotionsCache. Function checks this cache before + * make any calculations. + * + * @param height height of movement or resizing (px) + * @param speed speed coefficient + * @param prefix prefix of the key in the cache. Must be any string with + * trailing "_" caracter. + * + * @return array of delays before steps of animation + */ + function calculateDelays(height, speed, prefix) { + var key = prefix + height + '_' + speed, + deltas = [], // time between steps of animation + delays = [], // delays before steps from beginning of animation + totalDelay = 0, + l, + i; + + /* Check the cache */ + if (_bouncingMotionsCache[key]) { + return _bouncingMotionsCache[key]; + } + + /* Calculate delta time for bouncing animation */ + + /* Delta time to movement in one direction */ + deltas[height] = speed; + deltas[0] = 0; + i = height; + while (--i) { + deltas[i] = Math.round(speed / (height - i)); + } + + /* Delta time for movement in two directions */ + i = height; + while (i--) { + deltas.push(deltas[i]); + } + + /* Calculate move delays (cumulated deltas) */ + // TODO: instead of deltas.lenght write bounceHeight * 2 - 1 + for (i = 0, l = deltas.length; i < l; i++) { + totalDelay += deltas[i]; + delays.push(totalDelay); + } + + /* Save move delays to cache */ + _bouncingMotionsCache[key] = delays; + + return delays; + } + + /* ------------------------------------------------------------------------- + * Class "static" methods + * ------------------------------------------------------------------------- + */ + + L.Marker._bouncingMarkers = []; // array of bouncing markers + + /** + * Registers default options of bouncing animation. + * + * @param options object with options + */ + L.Marker.setBouncingOptions = function(options) { + L.extend(L.Marker.prototype._bouncingOptions, options); + }; + + /** + * Returns array of currently bouncing markers. + * + * @return array of bouncing markers + */ + L.Marker.getBouncingMarkers = function() { + return L.Marker._bouncingMarkers; + }; + + /** + * Stops the bouncing of all currently bouncing markers. Purge the array of + * bouncing markers. + */ + L.Marker.stopAllBouncingMarkers = function() { + var marker; + while (marker = L.Marker._bouncingMarkers.shift()) { + marker._bouncingMotion.isBouncing = false; // stop bouncing + } + }; + + /** + * Adds the marker to the list of bouncing markers. If flag 'exclusive' is + * set to true, stops all bouncing markers before. + * + * @param marker marker object + * @param exclusive flag of exclusive bouncing. If set to true, stops the + * bouncing of all other markers. + */ + L.Marker._addBouncingMarker = function(marker, exclusive) { + if (exclusive || marker._bouncingOptions.exclusive) { + L.Marker.stopAllBouncingMarkers(); + } else { + L.Marker._stopEclusiveMarkerBouncing(); + } + L.Marker._bouncingMarkers.push(marker); + }; + + /** + * Removes the marker from the list of bouncing markers. + * + * @param marker marker object + */ + L.Marker._removeBouncingMarker = function(marker) { + var i = L.Marker._bouncingMarkers.length; + + if (i) { + while (i--) { + if (L.Marker._bouncingMarkers[i] == marker) { + L.Marker._bouncingMarkers.splice(i, 1); + break; + } + } + } + }; + + /** + * Stops the bouncing of exclusive marker. + */ + L.Marker._stopEclusiveMarkerBouncing = function() { + var i = L.Marker._bouncingMarkers.length; + + if (i) { + while (i--) { + if (L.Marker._bouncingMarkers[i]._bouncingOptions.exclusive) { + L.Marker._bouncingMarkers[i]._bouncingMotion.isBouncing = + false; // stop bouncing + L.Marker._bouncingMarkers.splice(i, 1); + } + } + } + }; + + /* ------------------------------------------------------------------------- + * L.Marker.prototype methods (shared by all instances) + * ------------------------------------------------------------------------- + */ + + L.Marker.include({ + + /* Default bouncing animation properties */ + _bouncingOptions: { + bounceHeight : 15, // how high marker can bounce (px) + contractHeight : 12, // how much marker can contract (px) + bounceSpeed : 52, // bouncing speed coefficient + contractSpeed : 52, // contracting speed coefficient + shadowAngle : - Math.PI / 4, // shadow inclination angle + // (radians); null value annulates + // shadow movement + elastic : true, // activate contract animation + exclusive : false, // many markers can bounce in the same time + }, + + /** + * Registers options of bouncing animation for this marker. After + * registration of options for concreet marker, it will ignore the + * changes of default options. Function automatically recalculates + * animation steps and delays. + * + * @param options options object + * + * @return this marker + */ + setBouncingOptions: function(options) { + + /* If _bouncingOptions was not redefined yet for this marker create + * own property and clone _bouncingOptions of prototype. + */ + if (!this.hasOwnProperty('_bouncingOptions')) { + this._bouncingOptions = L.extend( + {}, + L.Marker.prototype._bouncingOptions + ); + } + + /* Copy options passed as param */ + L.extend(this._bouncingOptions, options); + + /* Recalculate steps & delays of movement & resize animations */ + this._calculateTimeline(); + + /* Recalculate transformations */ + this._calculateTransforms(); + + return this; // fluent API + }, + + /** + * Returns true if this marker is bouncing. If this marker is not + * bouncing returns false. + * + * @return true if marker is bouncing, false if not + */ + isBouncing: function() { + return this._bouncingMotion.isBouncing; + }, + + /** + * Let's bounce now! + * + * @param times number of animation repeations (optional) + * + * @return this marker + */ + bounce: function() { + var marker = this, + icon = this._icon, + shadow = this._shadow, + + bouncingOptions = marker._bouncingOptions, + motion = marker._bouncingMotion, + + bounceHeight = bouncingOptions.bounceHeight, + contractHeight = bouncingOptions.contractHeight, + bounceSpeed = bouncingOptions.bounceSpeed, + contractSpeed = bouncingOptions.contractSpeed, + shadowAngle = bouncingOptions.shadowAngle, + elastic = bouncingOptions.elastic, + exclusive = bouncingOptions.exclusive, + + moveSteps = motion.moveSteps, + moveDelays = motion.moveDelays, + resizeSteps = motion.resizeSteps, + resizeDelays = motion.resizeDelays, + + nbMoveSteps = moveSteps.length, + nbResizeSteps = resizeSteps.length, + + baseIconCssText = motion.baseIconCssText, + baseShadowCssText = motion.baseShadowCssText, + + is3d = L.Browser.any3d, + + times = null; // null for infinite bouncing + + if (motion.bouncingAnimationPlaying) { + motion.isBouncing = true; + return; + } + + if (arguments.length == 1) { + times = arguments[0]; + } + + /** + * Makes the step of the movement animation. + * + * @param step step number + */ + function makeMoveStep(step) { + + /* Reset icon's cssText */ + icon.style.cssText = baseIconCssText + + 'z-index: ' + marker._zIndex + ';' + + transform + ': ' + motion.iconMoveTransforms[step]; + + /* Reset shadow's cssText */ + if (shadow) { + shadow.style.cssText = baseShadowCssText + + transform + ': ' + + motion.shadowMoveTransforms[step]; + } + } + + /** + * Makes the step of the movement animation in no 3D able web + * browser. + * + * @param step step number + */ + function makeMoveStepNo3D(step) { + + /* Reset icon's cssText */ + icon.style.cssText = baseIconCssText + + 'z-index: ' + marker._zIndex + ';'; + icon.style.left = motion.iconMovePoints[step][0] + 'px'; + icon.style.top = motion.iconMovePoints[step][1] + 'px'; + + /* Reset shadow's cssText */ + if (shadow) { + shadow.style.cssText = baseShadowCssText; + shadow.style.left = motion.shadowMovePoints[step][0] + 'px'; + shadow.style.top = motion.shadowMovePoints[step][1] + 'px'; + } + } + + /** + * Makes the step of resizing animation. + * + * @param step step number + */ + function makeResizeStep(step) { + + /* Reset icon's cssText */ + icon.style.cssText = baseIconCssText + + 'z-index: ' + marker._zIndex + ';' + + transform + ': ' + motion.iconResizeTransforms[step]; + + /* Reset shadow's cssText */ + if (shadow && shadowAngle != null) { + shadow.style.cssText = baseShadowCssText + + transform + ': ' + + motion.shadowResizeTransforms[step]; + } + } + + /** + * Moves the marker up & down. + */ + function move() { + if (times !== null) { + if (!--times) { + motion.isBouncing = false; // this is the last bouncing + motion.bouncingAnimationPlaying = false; + } + } + + var i = nbMoveSteps; + + /* Lauch timeouts for every step of the movement animation */ + if (is3d) { + while (i--) { + setTimeout( + makeMoveStep, + moveDelays[i], + moveSteps[i]); + } + } else { + while (i--) { + setTimeout( + makeMoveStepNo3D, + moveDelays[i], + moveSteps[i]); + } + } + + /* At the end of movement animation check if continue the + * bouncing with rezise animation, move animation or stop it. + */ + // TODO: longer timeout if there is not resize part of animation + setTimeout(function() { + if (elastic && is3d) { + resize(); // possible only in 3D able browsers + } else if (motion.isBouncing) { + setTimeout(move, bounceSpeed); + } else { + motion.bouncingAnimationPlaying = false; + } + }, moveDelays[nbMoveSteps - 1]); + } + + /** + * Contracts & expands the marker. + */ + function resize() { + var i = nbResizeSteps; + + /* Stop animation at the end if necessary */ + setTimeout(function () { + if (!motion.isBouncing) { + motion.bouncingAnimationPlaying = false; + } + }, resizeDelays[i]); + + /* Lauch timeouts for every step of the contraction animation */ + while (i--) { + setTimeout( + makeResizeStep, + resizeDelays[i], + resizeSteps[i]); + } + + /* At the end of contraction animation check if continue the + * bouncing with move animation or stop it. + */ + setTimeout(function() { + if (motion.isBouncing) { + move(); + } + }, resizeDelays[nbResizeSteps - 1]); + } + + L.Marker._addBouncingMarker(marker, exclusive); + motion.isBouncing = true; + motion.bouncingAnimationPlaying = true; + move(); // start animation + + return marker; // fluent API + }, + + /** + * Stops bouncing of this marker. Note: the bouncing not stops + * immediatly after the call of this method. Instead, the animation is + * executed until marker returns to it's original position and takes + * it's full size. + * + * @return this marker + */ + stopBouncing: function() { + this._bouncingMotion.isBouncing = false; + L.Marker._removeBouncingMarker(this); + + return this; // fluent API + }, + + /** + * Starts/stops bouncing of this marker. + * + * @return this marker + */ + toggleBouncing: function() { + if (this._bouncingMotion.isBouncing) { + this.stopBouncing(); + } else { + this.bounce(); + } + + return this; // fluent API + }, + + /** + * Calculates moveSteps, moveDelays, resizeSteps & resizeDelays for + * animation of this marker. + */ + _calculateTimeline: function() { + + /* + * Animation is defined by shifts of the marker from it's original + * position. Each step of the animation is a shift of 1px. + * + * We define function f(x) - time of waiting between shift of x px + * and shift of x+1 px. + * + * We use for this the inverse function f(x) = a / x; where a is the + * animation speed and x is the shift from original position in px. + */ + + /* recalculate steps & delays of movement & resize animations */ + this._bouncingMotion.moveSteps = calculateSteps( + this._bouncingOptions.bounceHeight, + 'moveSteps_' + ); + + this._bouncingMotion.moveDelays = calculateDelays( + this._bouncingOptions.bounceHeight, + this._bouncingOptions.bounceSpeed, + 'moveDelays_' + ); + + /* Calculate resize steps & delays only if elastic animation is + * enabled */ + if (this._bouncingOptions.elastic) { + this._bouncingMotion.resizeSteps = calculateSteps( + this._bouncingOptions.contractHeight, + 'resizeSteps_' + ); + + this._bouncingMotion.resizeDelays = calculateDelays( + this._bouncingOptions.contractHeight, + this._bouncingOptions.contractSpeed, + 'resizeDelays_' + ); + } + }, + + /** + * Calculated the transformations of this marker. + */ + _calculateTransforms: function() { + if (L.Browser.any3d) { + + /* Calculate transforms for 3D browsers */ + + if (this.options.icon.options.iconSize) { + var iconHeight = this.options.icon.options.iconSize[1]; + } else { + + /* To fix the case when icon is in _iconObj */ + var iconHeight = this._iconObj.options.iconSize[1]; + } + + /* Calculate move transforms of icon */ + this._bouncingMotion.iconMoveTransforms = + calculateIconMoveTransforms( + this._bouncingMotion.x, + this._bouncingMotion.y, + this._bouncingOptions.bounceHeight + ); + + /* Calculate resize transforms of icon */ + this._bouncingMotion.iconResizeTransforms = + calculateIconResizeTransforms( + this._bouncingMotion.x, + this._bouncingMotion.y, + iconHeight, + this._bouncingOptions.contractHeight + ); + + if (this._shadow) { + + /* Calculate move transformations of shadow */ + this._bouncingMotion.shadowMoveTransforms = + calculateShadowMoveTransforms( + this._bouncingMotion.x, + this._bouncingMotion.y, + this._bouncingOptions.bounceHeight, + this._bouncingOptions.shadowAngle + ); + + /* Calculate resize transforms of shadow */ + // TODO: use function calculateShadowResizeTransforms + this._bouncingMotion.shadowResizeTransforms = + calculateIconResizeTransforms( + this._bouncingMotion.x, + this._bouncingMotion.y, + this.options.icon.options.shadowSize[1], + this._bouncingOptions.contractHeight + ); + } + + } else { + + /* Calculate move points */ + + /* For the icon */ + this._bouncingMotion.iconMovePoints = + calculateIconMovePoints( + this._bouncingMotion.x, + this._bouncingMotion.y, + this._bouncingOptions.bounceHeight + ); + + /* And for the shadow */ + this._bouncingMotion.shadowMovePoints = + calculateShadowMovePoints( + this._bouncingMotion.x, + this._bouncingMotion.y, + this._bouncingOptions.bounceHeight, + this._bouncingOptions.shadowAngle + ); + + } + } + + }); + + /** + * Add init hook to calculate animation timeline. + */ + L.Marker.addInitHook(function() { + this._bouncingMotion = { + isBouncing: false, + bouncingAnimationPlaying: false + }; + this._calculateTimeline(); + }); + + // TODO: decide to redeclare ether only public or only private methods + var oldSetPos = L.Marker.prototype._setPos; + var oldOnAdd = L.Marker.prototype.onAdd; + var oldSetIcon = L.Marker.prototype.setIcon; + var oldOn = L.Marker.prototype._on; + + /** + * Redeclaration of _setPos function. + * + * @param pos position object + */ + L.Marker.prototype._setPos = function(pos) { + oldSetPos.call(this, pos); + this._bouncingMotion.x = pos.x; + this._bouncingMotion.y = pos.y; + this._calculateTransforms(); + }; + + /** + * Redeclaration of onAdd function. + * + * @param map map object + */ + L.Marker.prototype.onAdd = function(map) { + oldOnAdd.call(this, map); + + /* Create base cssText */ + var styles = parseCssText(this._icon.style.cssText); + delete styles[transform]; // delete old trasform style definition + delete styles['z-index']; // delete old z-index + + /* Restores opacity when marker (re)added : + * 1) checks opacityWhenUnclustered option used by cluster plugin + * 2) checks opacity option + * 3) assumes opacity is 1 */ + styles.opacity = this.options.opacityWhenUnclustered + || this.options.opacity + || 1; + + this._bouncingMotion.baseIconCssText = renderCssText(styles); + + /* Create base cssText for shadow */ + if (this._shadow) { + styles = parseCssText(this._shadow.style.cssText); + delete styles[transform]; // delete old trasform style definition + delete styles['opacity']; + this._bouncingMotion.baseShadowCssText = renderCssText(styles); + } + + this._bouncingMotion.bouncingAnimationPlaying = false; + if (this._bouncingMotion.isBouncing) { + this.bounce(); + } + }; + + L.Marker.prototype.setIcon = function(icon) { + oldSetIcon.call(this, icon); + + /* Create base cssText */ +if (this._icon) { + var styles = parseCssText(this._icon.style.cssText); + delete styles[transform]; // delete old trasform style definition + delete styles['z-index']; // delete old z-index + + /* Restores opacity when marker (re)added : + * 1) checks opacityWhenUnclustered option used by cluster plugin + * 2) checks opacity option + * 3) assumes opacity is 1 */ + styles.opacity = this.options.opacityWhenUnclustered + || this.options.opacity + || 1; +} + this._bouncingMotion.baseIconCssText = renderCssText(styles); + + if (this._shadow) { + styles = parseCssText(this._shadow.style.cssText); + delete styles[transform]; // delete old trasform style definition + delete styles['opacity']; + this._bouncingMotion.baseShadowCssText = renderCssText(styles); + } + }; + + L.Marker.prototype._on = function (type, fn, context) { + var newFn = function (event) { + if (type === 'click') { + document.activeElement.blur(); + } + + fn.call(this, event); + }; + + oldOn.call(this, type, newFn, context); + }; + +})(L); diff --git a/static/js/vendor/markerclusterer.js b/static/js/vendor/markerclusterer.js deleted file mode 100644 index 637c85228..000000000 --- a/static/js/vendor/markerclusterer.js +++ /dev/null @@ -1,1572 +0,0 @@ -// ==ClosureCompiler== -// @compilation_level ADVANCED_OPTIMIZATIONS -// @externs_url https://raw.githubusercontent.com/google/closure-compiler/master/contrib/externs/maps/google_maps_api_v3.js -// ==/ClosureCompiler== - -/** - * @name Gmaps MarkerClusterer for Google Maps v3 - * @version version 1.2.0 - * @author Luke Mahe - * @author Sebastian Hösl - * @fileoverview - * The library creates and manages per-zoom-level clusters for large amounts of - * markers. - * This is a v3 implementation of the v2 MarkerClusterer - * {@link http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/} - * >. - */ - -/** - * @license - * - * 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. - */ - -/** - * A Marker Clusterer that clusters markers. - * - * @param {google.maps.Map} map The Google map to attach to. - * @param {Array.=} opt_markers Optional markers to add to - * the cluster. - * @param {Object=} opt_options support the following options: - * 'gridSize': (number) The grid size of a cluster in pixels. - * 'maxZoom': (number) The maximum zoom level that a marker can be part of a - * cluster. - * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a - * cluster is to zoom into it. - * 'averageCenter': (boolean) Whether the center of each cluster should be - * the average of all markers in the cluster. - * 'minimumClusterSize': (number) The minimum number of markers to be in a - * cluster before the markers are hidden and a count - * is shown. - * 'ignoreHiddenMarkers': (boolean) Whether to ignore markers that are not - * visible or count and cluster them anyway - * 'styles': (Array) An array of objects with these properties: - * 'url': (string) The image url. - * 'height': (number) The image height. - * 'width': (number) The image width. - * 'anchor': (Array) The anchor position of the label text. - * 'textColor': (string) The text color. - * 'textSize': (number) The text size. - * 'backgroundPosition': (string) The position of the backgound x, y. - * 'iconAnchor': (Array) The anchor position of the icon x, y. - * 'cssClass': (string) One or more CSS class for styling this marker. - * 'onMouseoverCluster': (function) The event handler used for onmouseover - * each cluster - * 'onMouseoutCluster': (function) The event handler used for onmouseout - * each cluster - * 'drawCluster': (function) Custom draw method for ClusterIcon - * 'hideCluster': (function) Custom hide method for ClusterIcon - * 'showCluster': (function) Custom hide method for ClusterIcon - * 'onAddCluster': (function) Custom onAdd method for ClusterIcon - * 'onRemoveCluster': (function) Custom onRemove method for ClusterIcon - * @constructor - * @extends google.maps.OverlayView - */ -function MarkerClusterer(map, opt_markers, opt_options) { - // MarkerClusterer implements google.maps.OverlayView interface. We use the - // extend function to extend MarkerClusterer with google.maps.OverlayView - // because it might not always be available when the code is defined so we - // look for it at the last possible moment. If it doesn't exist now then - // there is no point going ahead :) - this.extend(MarkerClusterer, google.maps.OverlayView); - this.map_ = map; - - /** - * @type {Array.} - * @private - */ - this.markers_ = []; - - /** - * @type {Array.} - */ - this.clusters_ = []; - - /** - * @type {Object} holding information about every markers cluster - */ - this.markersCluster_ = {}; - - /** - * @type {Number} Unique markers ID - */ - this.markersUniqueID = 1; - - this.sizes = [53, 56, 66, 78, 90]; - - /** - * @private - */ - this.styles_ = []; - - /** - * @private - */ - this.cssClass_ = ''; - - /** - * @private - * @type {string} Set a default Cluster Class - */ - this.cssDefaultClass_ = 'cluster'; - - /** - * @private - */ - this.setIndex_ = 0; - - /** - * @type {boolean} - * @private - */ - this.ready_ = false; - - var options = opt_options || {}; - - /** - * @type {number} - * @private - */ - this.gridSize_ = (options['gridSize'] !== undefined) ? options['gridSize'] : 60; - - /** - * @private - */ - this.minClusterSize_ = options['minimumClusterSize'] || 2; - - /** - * @type {boolean} - * @private - */ - this.ignoreHiddenMarkers_ = options['ignoreHiddenMarkers'] || false; - - /** - * @type {?number} - * @private - */ - this.maxZoom_ = options['maxZoom'] || null; - - this.styles_ = options['styles'] || []; - - this.cssClass_ = options['cssClass'] || null; - - /** - * @type {string} - * @private - */ - this.imagePath_ = options['imagePath'] || - this.MARKER_CLUSTER_IMAGE_PATH_; - - /** - * @type {string} - * @private - */ - this.imageExtension_ = options['imageExtension'] || - this.MARKER_CLUSTER_IMAGE_EXTENSION_; - - /** - * @type {boolean} - * @private - */ - this.zoomOnClick_ = true; - - if (options['zoomOnClick'] != undefined) { - this.zoomOnClick_ = options['zoomOnClick']; - } - - /** - * @type {boolean} - * @private - */ - this.averageCenter_ = false; - - if (options['averageCenter'] != undefined) { - this.averageCenter_ = options['averageCenter']; - } - - /** - * @type {function} - * @private - */ - this.onMouseoverCluster_ = options['onMouseoverCluster']; - - /** - * @type {function} - * @private - */ - this.onMouseoutCluster_ = options['onMouseoutCluster']; - - /** - * @type {function} - * @private - */ - this.drawCluster_ = options['drawCluster']; - - /** - * @type {function} - * @private - */ - this.hideCluster_ = options['hideCluster']; - - /** - * @type {function} - * @private - */ - this.showCluster_ = options['showCluster']; - - /** - * @type {function} - * @private - */ - this.onAddCluster_ = options['onAddCluster']; - - /** - * @type {function} - * @private - */ - this.onRemoveCluster_ = options['onRemoveCluster']; - - this.setupStyles_(); - - this.setMap(map); - - /** - * @type {number} - * @private - */ - this.prevZoom_ = this.map_.getZoom(); - - // Add the map event listeners - var that = this; - google.maps.event.addListener(this.map_, 'zoom_changed', function() { - var zoom = that.map_.getZoom(); - - if (that.prevZoom_ != zoom) { - that.prevZoom_ = zoom; - that.resetViewport(); - } - }); - - google.maps.event.addListener(this.map_, 'idle', function() { - that.redraw(); - }); - - // Finally, add the markers - if (opt_markers && opt_markers.length) { - this.addMarkers(opt_markers, false); - } -} - - -/** - * The marker cluster image path. - * - * @type {string} - * @private - */ -MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = 'https://raw.githubusercontent.com/gmaps-marker-clusterer/gmaps-marker-clusterer/master/images/m'; - - -/** - * The marker cluster image path. - * - * @type {string} - * @private - */ -MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; - - -/** - * Extends a objects prototype by anothers. - * - * @param {Object} obj1 The object to be extended. - * @param {Object} obj2 The object to extend with. - * @return {Object} The new extended object. - * @ignore - */ -MarkerClusterer.prototype.extend = function(obj1, obj2) { - return (function(object) { - for (var property in object.prototype) { - this.prototype[property] = object.prototype[property]; - } - return this; - }).apply(obj1, [obj2]); -}; - - -/** - * Implementaion of the interface method. - * @ignore - */ -MarkerClusterer.prototype.onAdd = function() { - this.setReady_(true); -}; - -/** - * Implementaion of the interface method. - * @ignore - */ -MarkerClusterer.prototype.draw = function() {}; - -/** - * Sets up the styles object. - * - * @private - */ -MarkerClusterer.prototype.setupStyles_ = function() { - if (this.styles_.length) { - return; - } - - for (var i = 0, size; size = this.sizes[i]; i++) { - var url = ''; - if (typeof this.imagePath_ === 'function') { - url = this.imagePath_(i, size); - } else { - url = this.imagePath_ + (i + 1) + '.' + this.imageExtension_; - } - this.styles_.push({ - url: url, - height: size, - width: size - }); - } -}; - -/** - * Fit the map to the bounds of the markers in the clusterer. - */ -MarkerClusterer.prototype.fitMapToMarkers = function() { - var markers = this.getMarkers(); - var bounds = new google.maps.LatLngBounds(); - for (var i = 0, marker; marker = markers[i]; i++) { - bounds.extend(marker.getPosition()); - } - - this.map_.fitBounds(bounds); -}; - - -/** - * Sets the styles. - * - * @param {Object} styles The style to set. - */ -MarkerClusterer.prototype.setStyles = function(styles) { - this.styles_ = styles; -}; - - -/** - * Gets the styles. - * - * @return {Object} The styles object. - */ -MarkerClusterer.prototype.getStyles = function() { - return this.styles_; -}; - - -/** - * Whether zoom on click is set. - * - * @return {boolean} True if zoomOnClick_ is set. - */ -MarkerClusterer.prototype.isZoomOnClick = function() { - return this.zoomOnClick_; -}; - -/** - * Whether average center is set. - * - * @return {boolean} True if averageCenter_ is set. - */ -MarkerClusterer.prototype.isAverageCenter = function() { - return this.averageCenter_; -}; - - -/** - * Returns the array of markers in the clusterer. - * - * @return {Array.} The markers. - */ -MarkerClusterer.prototype.getMarkers = function() { - return this.markers_; -}; - - -/** - * Returns the number of markers in the clusterer - * - * @return {Number} The number of markers. - */ -MarkerClusterer.prototype.getTotalMarkers = function() { - return this.markers_.length; -}; - - -/** - * Sets the max zoom for the clusterer. - * - * @param {number} maxZoom The max zoom level. - */ -MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { - this.maxZoom_ = maxZoom; -}; - - -/** - * Gets the max zoom for the clusterer. - * - * @return {number} The max zoom level. - */ -MarkerClusterer.prototype.getMaxZoom = function() { - return this.maxZoom_; -}; - -/** - * Gets marker's cluster object based on given marker - * - * @param {google.maps.Marker} marker - * - * @return {Cluster} - */ -MarkerClusterer.prototype.getMarkersCluster = function(marker) { - return this.clusters_[this.markersCluster_[marker.uniqueID]]; -}; - -/** - * The function for calculating the cluster icon image. - * - * @param {Array.} markers The markers in the clusterer. - * @param {number} numStyles The number of styles available. - * @return {Object} A object properties: 'text' (string) and 'index' (number). - * @private - */ -MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { - var index = 0; - var count = markers.length; - var dv = count; - while (dv !== 0) { - dv = parseInt(dv / 10, 10); - index++; - } - - index = Math.min(index, numStyles); - - return { - text: count, - index: index - }; -}; - - -/** - * Set the calculator function. - * - * @param {function(Array, number)} calculator The function to set as the - * calculator. The function should return a object properties: - * 'text' (string) and 'index' (number). - * - */ -MarkerClusterer.prototype.setCalculator = function(calculator) { - this.calculator_ = calculator; -}; - - -/** - * Get the calculator function. - * - * @return {function(Array, number)} the calculator function. - */ -MarkerClusterer.prototype.getCalculator = function() { - return this.calculator_; -}; - - -/** - * Add an array of markers to the clusterer. - * - * @param {Array.} markers The markers to add. - * @param {boolean=} opt_nodraw Whether to redraw the clusters. - */ -MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { - for (var i = 0, marker; marker = markers[i]; i++) { - this.pushMarkerTo_(marker); - } - if (!opt_nodraw) { - this.redraw(); - } -}; - - -/** - * Pushes a marker to the clusterer. - * - * @param {google.maps.Marker} marker The marker to add. - * @private - */ -MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { - marker.isAdded = false; - if (marker['draggable']) { - // If the marker is draggable add a listener so we update the clusters on - // the drag end. - var that = this; - google.maps.event.addListener(marker, 'dragend', function() { - marker.isAdded = false; - that.repaint(); - }); - } - marker.uniqueID = this.markersUniqueID; - this.markersUniqueID++; - this.markers_.push(marker); -}; - - -/** - * Adds a marker to the clusterer and redraws if needed. - * - * @param {google.maps.Marker} marker The marker to add. - * @param {boolean=} opt_nodraw Whether to redraw the clusters. - */ -MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { - this.pushMarkerTo_(marker); - if (!opt_nodraw) { - this.redraw(); - } -}; - - -/** - * Removes a marker and returns true if removed, false if not - * - * @param {google.maps.Marker} marker The marker to remove - * @return {boolean} Whether the marker was removed or not - * @private - */ -MarkerClusterer.prototype.removeMarker_ = function(marker) { - var index = -1; - if (this.markers_.indexOf) { - index = this.markers_.indexOf(marker); - } else { - for (var i = 0, m; m = this.markers_[i]; i++) { - if (m == marker) { - index = i; - break; - } - } - } - - if (index == -1) { - // Marker is not in our list of markers. - return false; - } - - marker.setMap(null); - - this.markers_.splice(index, 1); - delete this.markersCluster_[marker.uniqueID]; - - return true; -}; - - -/** - * Remove a marker from the cluster. - * - * @param {google.maps.Marker} marker The marker to remove. - * @param {boolean=} opt_nodraw Optional boolean to force no redraw. - * @return {boolean} True if the marker was removed. - */ -MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { - var removed = this.removeMarker_(marker); - - if (!opt_nodraw && removed) { - this.resetViewport(); - this.redraw(); - return true; - } else { - return false; - } -}; - - -/** - * Removes an array of markers from the cluster. - * - * @param {Array.} markers The markers to remove. - * @param {boolean=} opt_nodraw Optional boolean to force no redraw. - */ -MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { - var removed = false; - - for (var i = markers.length; i >= 0; i--) { - var marker = markers[i]; - var r = this.removeMarker_(marker); - removed = removed || r; - } - - if (!opt_nodraw && removed) { - this.resetViewport(); - this.redraw(); - return true; - } -}; - - -/** - * Sets the clusterer's ready state. - * - * @param {boolean} ready The state. - * @private - */ -MarkerClusterer.prototype.setReady_ = function(ready) { - if (!this.ready_) { - this.ready_ = ready; - this.createClusters_(); - } -}; - - -/** - * Returns the number of clusters in the clusterer. - * - * @return {number} The number of clusters. - */ -MarkerClusterer.prototype.getTotalClusters = function() { - return this.clusters_.length; -}; - - -/** - * Returns the list of clusters in the clusterer - * - * @return {Array.} The list of clusters. - */ -MarkerClusterer.prototype.getClusters = function() { - return this.clusters_; -}; - - -/** - * Returns the google map that the clusterer is associated with. - * - * @return {google.maps.Map} The map. - */ -MarkerClusterer.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Sets the google map that the clusterer is associated with. - * - * @param {google.maps.Map} map The map. - */ -MarkerClusterer.prototype.setMap = function(map) { - this.map_ = map; -}; - - -/** - * Returns the size of the grid. - * - * @return {number} The grid size. - */ -MarkerClusterer.prototype.getGridSize = function() { - return this.gridSize_; -}; - - -/** - * Sets the size of the grid. - * - * @param {number} size The grid size. - */ -MarkerClusterer.prototype.setGridSize = function(size) { - this.gridSize_ = size; -}; - - -/** - * Returns the min cluster size. - * - * @return {number} The grid size. - */ -MarkerClusterer.prototype.getMinClusterSize = function() { - return this.minClusterSize_; -}; - -/** - * Sets the min cluster size. - * - * @param {number} size The grid size. - */ -MarkerClusterer.prototype.setMinClusterSize = function(size) { - this.minClusterSize_ = size; -}; - - -/** - * Extends a bounds object by the grid size. - * - * @param {google.maps.LatLngBounds} bounds The bounds to extend. - * @return {google.maps.LatLngBounds} The extended bounds. - */ -MarkerClusterer.prototype.getExtendedBounds = function(bounds) { - var projection = this.getProjection(); - - // Turn the bounds into latlng. - var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), - bounds.getNorthEast().lng()); - var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), - bounds.getSouthWest().lng()); - - // Convert the points to pixels and the extend out by the grid size. - var trPix = projection.fromLatLngToDivPixel(tr); - trPix.x += this.gridSize_; - trPix.y -= this.gridSize_; - - var blPix = projection.fromLatLngToDivPixel(bl); - blPix.x -= this.gridSize_; - blPix.y += this.gridSize_; - - // Convert the pixel points back to LatLng - var ne = projection.fromDivPixelToLatLng(trPix); - var sw = projection.fromDivPixelToLatLng(blPix); - - // Extend the bounds to contain the new bounds. - bounds.extend(ne); - bounds.extend(sw); - - return bounds; -}; - - -/** - * Determins if a marker is contained in a bounds. - * - * @param {google.maps.Marker} marker The marker to check. - * @param {google.maps.LatLngBounds} bounds The bounds to check against. - * @return {boolean} True if the marker is in the bounds. - * @private - */ -MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { - return bounds.contains(marker.getPosition()); -}; - - -/** - * Clears all clusters and markers from the clusterer. - */ -MarkerClusterer.prototype.clearMarkers = function() { - this.resetViewport(true); - - // Set the markers a empty array. - this.markers_ = []; - this.markersCluster_ = {}; - this.markersUniqueID = 1; -}; - - -/** - * Clears all existing clusters and recreates them. - * @param {boolean} opt_hide To also hide the marker. - */ -MarkerClusterer.prototype.resetViewport = function(opt_hide) { - // Remove all the clusters - for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { - cluster.remove(); - } - - // Reset the markers to not be added and to be invisible. - for (var i = 0, marker; marker = this.markers_[i]; i++) { - marker.isAdded = false; - if (opt_hide) { - marker.setMap(null); - } - } - - this.clusters_ = []; - this.markersCluster_ = {}; - this.markersUniqueID = 1; -}; - -/** - * - */ -MarkerClusterer.prototype.repaint = function() { - var oldClusters = this.clusters_.slice(); - this.clusters_.length = 0; - this.resetViewport(); - this.redraw(); - - // Remove the old clusters. - // Do it in a timeout so the other clusters have been drawn first. - window.setTimeout(function() { - for (var i = 0, cluster; cluster = oldClusters[i]; i++) { - cluster.remove(); - } - }, 0); -}; - - -/** - * Redraws the clusters. - */ -MarkerClusterer.prototype.redraw = function() { - this.createClusters_(); -}; - - -/** - * Calculates the distance between two latlng locations in km. - * @see http://www.movable-type.co.uk/scripts/latlong.html - * - * @param {google.maps.LatLng} p1 The first lat lng point. - * @param {google.maps.LatLng} p2 The second lat lng point. - * @return {number} The distance between the two points in km. - * @private - */ -MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { - if (!p1 || !p2) { - return 0; - } - - var R = 6371; // Radius of the Earth in km - var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; - var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; - var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + - Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * - Math.sin(dLon / 2) * Math.sin(dLon / 2); - var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); - var d = R * c; - return d; -}; - - -/** - * Add a marker to a cluster, or creates a new cluster. - * - * @param {google.maps.Marker} marker The marker to add. - * @private - */ -MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { - var distance = 40000; // Some large number - var clusterToAddTo = null; - var pos = marker.getPosition(); - var clusterIndex = null; - for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { - var center = cluster.getCenter(); - if (center) { - var d = this.distanceBetweenPoints_(center, marker.getPosition()); - if (d < distance) { - distance = d; - clusterToAddTo = cluster; - clusterIndex = i; - } - } - } - - if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { - clusterToAddTo.addMarker(marker); - } else { - var cluster = new Cluster(this); - cluster.addMarker(marker); - this.clusters_.push(cluster); - clusterIndex = this.clusters_.length - 1; - } - - if (marker.isAdded) { - this.markersCluster_[marker.uniqueID] = clusterIndex; - } -}; - - -/** - * Creates the clusters. - * - * @private - */ -MarkerClusterer.prototype.createClusters_ = function() { - if (!this.ready_) { - return; - } - - // Get our current map view bounds. - // Create a new bounds object so we don't affect the map. - var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), - this.map_.getBounds().getNorthEast()); - var bounds = this.getExtendedBounds(mapBounds); - - for (var i = 0, marker; marker = this.markers_[i]; i++) { - if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds) && (!this.ignoreHiddenMarkers_ || marker.getVisible())) { - this.addToClosestCluster_(marker); - } - } -}; - - -/** - * A cluster that contains markers. - * - * @param {MarkerClusterer} markerClusterer The markerclusterer that this - * cluster is associated with. - * @constructor - * @ignore - */ -function Cluster(markerClusterer) { - this.markerClusterer_ = markerClusterer; - this.map_ = markerClusterer.getMap(); - this.gridSize_ = markerClusterer.getGridSize(); - this.minClusterSize_ = markerClusterer.getMinClusterSize(); - this.averageCenter_ = markerClusterer.isAverageCenter(); - this.center_ = null; - this.markers_ = []; - this.bounds_ = null; - this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), - markerClusterer.getGridSize()); -} - -/** - * Determins if a marker is already added to the cluster. - * - * @param {google.maps.Marker} marker The marker to check. - * @return {boolean} True if the marker is already added. - */ -Cluster.prototype.isMarkerAlreadyAdded = function(marker) { - if (this.markers_.indexOf) { - return this.markers_.indexOf(marker) != -1; - } else { - for (var i = 0, m; m = this.markers_[i]; i++) { - if (m == marker) { - return true; - } - } - } - return false; -}; - - -/** - * Add a marker the cluster. - * - * @param {google.maps.Marker} marker The marker to add. - * @return {boolean} True if the marker was added. - */ -Cluster.prototype.addMarker = function(marker) { - if (this.isMarkerAlreadyAdded(marker)) { - return false; - } - - if (!this.center_) { - this.center_ = marker.getPosition(); - this.calculateBounds_(); - } else { - if (this.averageCenter_) { - var l = this.markers_.length + 1; - var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l; - var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l; - this.center_ = new google.maps.LatLng(lat, lng); - this.calculateBounds_(); - } - } - - marker.isAdded = true; - this.markers_.push(marker); - - var len = this.markers_.length; - if (len < this.minClusterSize_ && marker.getMap() != this.map_) { - // Min cluster size not reached so show the marker. - marker.setMap(this.map_); - } - - if (len == this.minClusterSize_) { - // Hide the markers that were showing. - for (var i = 0; i < len; i++) { - this.markers_[i].setMap(null); - } - } - - if (len >= this.minClusterSize_) { - marker.setMap(null); - } - - this.updateIcon(); - return true; -}; - - -/** - * Returns the marker clusterer that the cluster is associated with. - * - * @return {MarkerClusterer} The associated marker clusterer. - */ -Cluster.prototype.getMarkerClusterer = function() { - return this.markerClusterer_; -}; - - -/** - * Returns the bounds of the cluster. - * - * @return {google.maps.LatLngBounds} the cluster bounds. - */ -Cluster.prototype.getBounds = function() { - var bounds = new google.maps.LatLngBounds(this.center_, this.center_); - var markers = this.getMarkers(); - for (var i = 0, marker; marker = markers[i]; i++) { - bounds.extend(marker.getPosition()); - } - return bounds; -}; - - -/** - * Removes the cluster - */ -Cluster.prototype.remove = function() { - this.clusterIcon_.remove(); - this.markers_.length = 0; - delete this.markers_; -}; - - -/** - * Returns the center of the cluster. - * - * @return {number} The cluster center. - */ -Cluster.prototype.getSize = function() { - return this.markers_.length; -}; - - -/** - * Returns the center of the cluster. - * - * @return {Array.} The cluster center. - */ -Cluster.prototype.getMarkers = function() { - return this.markers_; -}; - - -/** - * Returns the center of the cluster. - * - * @return {google.maps.LatLng} The cluster center. - */ -Cluster.prototype.getCenter = function() { - return this.center_; -}; - - -/** - * Calculated the extended bounds of the cluster with the grid. - * - * @private - */ -Cluster.prototype.calculateBounds_ = function() { - var bounds = new google.maps.LatLngBounds(this.center_, this.center_); - this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); -}; - - -/** - * Determines if a marker lies in the clusters bounds. - * - * @param {google.maps.Marker} marker The marker to check. - * @return {boolean} True if the marker lies in the bounds. - */ -Cluster.prototype.isMarkerInClusterBounds = function(marker) { - return this.bounds_.contains(marker.getPosition()); -}; - - -/** - * Returns the map that the cluster is associated with. - * - * @return {google.maps.Map} The map. - */ -Cluster.prototype.getMap = function() { - return this.map_; -}; - - -/** - * Updates the cluster icon - */ -Cluster.prototype.updateIcon = function() { - var zoom = this.map_.getZoom(); - var mz = this.markerClusterer_.getMaxZoom(); - - if (mz && zoom > mz) { - // The zoom is greater than our max zoom so show all the markers in cluster. - for (var i = 0, marker; marker = this.markers_[i]; i++) { - marker.setMap(this.map_); - } - return; - } - - if (this.markers_.length < this.minClusterSize_) { - // Min cluster size not yet reached. - this.clusterIcon_.hide(); - return; - } - - var numStyles = this.markerClusterer_.getStyles().length; - var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); - this.clusterIcon_.setCenter(this.center_); - this.clusterIcon_.setSums(sums); - this.clusterIcon_.show(); -}; - - -/** - * A cluster icon - * - * @param {Cluster} cluster The cluster to be associated with. - * @param {Object} styles An object that has style properties: - * 'url': (string) The image url. - * 'height': (number) The image height. - * 'width': (number) The image width. - * 'anchor': (Array) The anchor position of the label text. - * 'textColor': (string) The text color. - * 'textSize': (number) The text size. - * 'backgroundPosition: (string) The background postition x, y. - * @param {number=} opt_padding Optional padding to apply to the cluster icon. - * @constructor - * @extends google.maps.OverlayView - * @ignore - */ -function ClusterIcon(cluster, styles, opt_padding) { - cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); - - this.styles_ = styles; - this.padding_ = opt_padding || 0; - this.cluster_ = cluster; - this.center_ = null; - this.map_ = cluster.getMap(); - this.div_ = null; - this.sums_ = null; - this.visible_ = false; - - this.setMap(this.map_); -} - -/** - * Triggers the clusterclick event and zoom's if the option is set. - * - * @param {google.maps.MouseEvent} event The event to propagate - */ -ClusterIcon.prototype.triggerClusterClick = function(event) { - var markerClusterer = this.cluster_.getMarkerClusterer(); - - // Trigger the clusterclick event. - google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_, event); - - if (markerClusterer.isZoomOnClick()) { - // Zoom into the cluster. - this.map_.fitBounds(this.cluster_.getBounds()); - } -}; - -/** - * Triggers the clustermouseover event. - * @param {google.maps.MouseEvent} event The event to propagate - */ -ClusterIcon.prototype.triggerClusterMouseover = function(event) { - var markerClusterer = this.cluster_.getMarkerClusterer(); - - // Trigger the clustermouseover event. - google.maps.event.trigger(markerClusterer, 'clustermouseover', this.cluster_, event); - - if (typeof this.cluster_.markerClusterer_.onMouseoverCluster_ === 'function') { - this.cluster_.markerClusterer_.onMouseoverCluster_(this, event); - } -}; - -/** - * Triggers the clustermouseout event. - * @param {google.maps.MouseEvent} event The event to propagate - */ -ClusterIcon.prototype.triggerClusterMouseout = function(event) { - var markerClusterer = this.cluster_.getMarkerClusterer(); - - // Trigger the clustermouseout event. - google.maps.event.trigger(markerClusterer, 'clustermouseout', this.cluster_, event); - - if (typeof this.cluster_.markerClusterer_.onMouseoutCluster_ === 'function') { - this.cluster_.markerClusterer_.onMouseoutCluster_(this, event); - } -}; - -/** - * Adding the cluster icon to the dom. - * @ignore - */ -ClusterIcon.prototype.onAdd = function () { - if (typeof this.cluster_.markerClusterer_.onAddCluster_ === 'function') { - this.cluster_.markerClusterer_.onAddCluster_(this); - } - else { - defaultClusterOnAdd(this); - } -}; - -/** - * Default onAdd function - * @ignore - */ -function defaultClusterOnAdd(clusterIcon) { - clusterIcon.div_ = document.createElement('DIV'); - if (clusterIcon.visible_) { - var pos = clusterIcon.getPosFromLatLng_(clusterIcon.center_); - clusterIcon.div_.style.cssText = clusterIcon.createCss(pos); - clusterIcon.div_.innerHTML = clusterIcon.sums_.text; - var markerClusterer = clusterIcon.cluster_.getMarkerClusterer(); - - if (markerClusterer.cssClass_) { - clusterIcon.div_.className = markerClusterer.cssClass_ + ' ' + markerClusterer.cssDefaultClass_ + clusterIcon.setIndex_; - } else { - clusterIcon.div_.className = markerClusterer.cssDefaultClass_ + clusterIcon.setIndex_; - } - } - - var panes = clusterIcon.getPanes(); - - panes.overlayMouseTarget.appendChild(clusterIcon.div_); - - var isDragging = false; - var isMouseDown = false; - - google.maps.event.addDomListener(clusterIcon.div_, 'click', function(event) { - // Only perform click when not preceded by a drag - if (!isDragging) { - clusterIcon.triggerClusterClick(event); - } - }); - - google.maps.event.addDomListener(clusterIcon.div_, 'mousedown', function() { - isDragging = false; - isMouseDown = true; - }); - - google.maps.event.addDomListener(clusterIcon.div_, 'mouseup', function() { - isDragging = false; - isMouseDown = false; - }); - - google.maps.event.addDomListener(clusterIcon.div_, 'mousemove', function() { - if (isMouseDown) { - isDragging = true; - } - }); - - google.maps.event.addDomListener(clusterIcon.div_, 'mouseover', function(event) { - clusterIcon.triggerClusterMouseover(event); - }); - - google.maps.event.addDomListener(clusterIcon.div_, 'mouseout', function(event) { - clusterIcon.triggerClusterMouseout(event); - }); -} - -/** - * Returns the position to place the div dending on the latlng. - * - * @param {google.maps.LatLng} latlng The position in latlng. - * @return {google.maps.Point} The position in pixels. - * @private - */ -ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { - var pos = this.getProjection().fromLatLngToDivPixel(latlng); - - if (typeof this.iconAnchor_ === 'object' && this.iconAnchor_.length === 2) { - pos.x -= this.iconAnchor_[0]; - pos.y -= this.iconAnchor_[1]; - } else { - pos.x -= parseInt(this.width_ / 2, 10); - pos.y -= parseInt(this.height_ / 2, 10); - } - return pos; -}; - - -/** - * Draw the icon. - * @ignore - */ -ClusterIcon.prototype.draw = function () { - if (typeof this.cluster_.markerClusterer_.drawCluster_ === 'function') { - this.cluster_.markerClusterer_.drawCluster_(this); - } - else { - defaultClusterDraw(this); - } -}; - -/** - * Default draw function - * @ignore - */ -function defaultClusterDraw(clusterIcon) { - if (clusterIcon.visible_) { - var pos = clusterIcon.getPosFromLatLng_(clusterIcon.center_); - clusterIcon.div_.style.top = pos.y + 'px'; - clusterIcon.div_.style.left = pos.x + 'px'; - } -} - - -/** - * Hide the icon. - */ -ClusterIcon.prototype.hide = function() { - if (typeof this.cluster_.markerClusterer_.hideCluster_ === 'function') { - this.cluster_.markerClusterer_.hideCluster_(this); - } - else { - defaultClusterHide(this); - } -}; - - -/** - * Default hide function - * @ignore - */ -function defaultClusterHide(clusterIcon) { - if (clusterIcon.div_) { - clusterIcon.div_.style.display = 'none'; - } - clusterIcon.visible_ = false; -} - - -/** - * Position and show the icon. - */ -ClusterIcon.prototype.show = function() { - if (typeof this.cluster_.markerClusterer_.showCluster_ === 'function') { - this.cluster_.markerClusterer_.showCluster_(this); - } - else { - defaultClusterShow(this); - } -}; - -/** - * Default show function - * @ignore - */ -function defaultClusterShow(clusterIcon) { - if (clusterIcon.div_) { - var pos = clusterIcon.getPosFromLatLng_(clusterIcon.center_); - clusterIcon.div_.style.cssText = clusterIcon.createCss(pos); - clusterIcon.div_.style.display = ''; - } - clusterIcon.visible_ = true; -} - - -/** - * Remove the icon from the map - */ -ClusterIcon.prototype.remove = function() { - this.setMap(null); -}; - - -/** - * Implementation of the onRemove interface. - * @ignore - */ -ClusterIcon.prototype.onRemove = function() { - if (typeof this.cluster_.markerClusterer_.onRemoveCluster_ === 'function') { - this.cluster_.markerClusterer_.onRemoveCluster_(this); - } - else { - defaultClusterOnRemove(this); - } -}; - -/** - * Default onRemove function - * @ignore - */ -function defaultClusterOnRemove(clusterIcon) { - if (clusterIcon.div_ && clusterIcon.div_.parentNode) { - clusterIcon.hide(); - clusterIcon.div_.parentNode.removeChild(clusterIcon.div_); - clusterIcon.div_ = null; - } -} - - -/** - * Set the sums of the icon. - * - * @param {Object} sums The sums containing: - * 'text': (string) The text to display in the icon. - * 'index': (number) The style index of the icon. - */ -ClusterIcon.prototype.setSums = function(sums) { - this.sums_ = sums; - this.text_ = sums.text; - this.index_ = sums.index; - if (this.div_) { - this.div_.innerHTML = sums.text; - } - - this.useStyle(); -}; - - -/** - * Sets the icon to the the styles. - */ -ClusterIcon.prototype.useStyle = function() { - var index = Math.max(0, this.sums_.index - 1); - index = Math.min(this.styles_.length - 1, index); - var style = this.styles_[index]; - this.url_ = style['url']; - this.height_ = style['height']; - this.width_ = style['width']; - this.textColor_ = style['textColor']; - this.anchor_ = style['anchor']; - this.textSize_ = style['textSize']; - this.backgroundPosition_ = style['backgroundPosition']; - this.iconAnchor_ = style['iconAnchor']; - this.setIndex_ = index; -}; - - -/** - * Sets the center of the icon. - * - * @param {google.maps.LatLng} center The latlng to set as the center. - */ -ClusterIcon.prototype.setCenter = function(center) { - this.center_ = center; -}; - - -/** - * Create the css text based on the position of the icon. - * - * @param {google.maps.Point} pos The position. - * @return {string} The css style text. - */ -ClusterIcon.prototype.createCss = function(pos) { - var style = []; - var markerClusterer = this.cluster_.getMarkerClusterer(); - - if (!markerClusterer.cssClass_) { - style.push('background-image:url(' + this.url_ + ');'); - var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; - style.push('background-position:' + backgroundPosition + ';'); - - if (typeof this.anchor_ === 'object') { - if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && - this.anchor_[0] < this.height_) { - style.push('height:' + (this.height_ - this.anchor_[0]) + - 'px; padding-top:' + this.anchor_[0] + 'px;'); - } else if (typeof this.anchor_[0] === 'number' && this.anchor_[0] < 0 && - -this.anchor_[0] < this.height_) { - style.push('height:' + this.height_ + 'px; line-height:' + (this.height_ + this.anchor_[0]) + - 'px;'); - } else { - style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + - 'px;'); - } - if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && - this.anchor_[1] < this.width_) { - style.push('width:' + (this.width_ - this.anchor_[1]) + - 'px; padding-left:' + this.anchor_[1] + 'px;'); - } else { - style.push('width:' + this.width_ + 'px; text-align:center;'); - } - } else { - style.push('height:' + this.height_ + 'px; line-height:' + - this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); - } - - var txtColor = this.textColor_ ? this.textColor_ : 'black'; - var txtSize = this.textSize_ ? this.textSize_ : 11; - - style.push('cursor:pointer; top:' + pos.y + 'px; left:' + - pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + - txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); - - } else { - style.push('top:' + pos.y + 'px; left:' + pos.x + 'px;'); - } - - return style.join(''); -}; - - -// Export Symbols for Closure -// If you are not going to compile with closure then you can remove the -// code below. -window['MarkerClusterer'] = MarkerClusterer; -MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker; -MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers; -MarkerClusterer.prototype['clearMarkers'] = - MarkerClusterer.prototype.clearMarkers; -MarkerClusterer.prototype['fitMapToMarkers'] = - MarkerClusterer.prototype.fitMapToMarkers; -MarkerClusterer.prototype['getCalculator'] = - MarkerClusterer.prototype.getCalculator; -MarkerClusterer.prototype['getGridSize'] = - MarkerClusterer.prototype.getGridSize; -MarkerClusterer.prototype['getExtendedBounds'] = - MarkerClusterer.prototype.getExtendedBounds; -MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap; -MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers; -MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom; -MarkerClusterer.prototype['getMarkersCluster'] = MarkerClusterer.prototype.getMarkersCluster; -MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles; -MarkerClusterer.prototype['getTotalClusters'] = - MarkerClusterer.prototype.getTotalClusters; -MarkerClusterer.prototype['getTotalMarkers'] = - MarkerClusterer.prototype.getTotalMarkers; -MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw; -MarkerClusterer.prototype['removeMarker'] = - MarkerClusterer.prototype.removeMarker; -MarkerClusterer.prototype['removeMarkers'] = - MarkerClusterer.prototype.removeMarkers; -MarkerClusterer.prototype['resetViewport'] = - MarkerClusterer.prototype.resetViewport; -MarkerClusterer.prototype['repaint'] = - MarkerClusterer.prototype.repaint; -MarkerClusterer.prototype['setCalculator'] = - MarkerClusterer.prototype.setCalculator; -MarkerClusterer.prototype['setGridSize'] = - MarkerClusterer.prototype.setGridSize; -MarkerClusterer.prototype['setMaxZoom'] = - MarkerClusterer.prototype.setMaxZoom; -MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd; -MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw; - -Cluster.prototype['getCenter'] = Cluster.prototype.getCenter; -Cluster.prototype['getSize'] = Cluster.prototype.getSize; -Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers; - -ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd; -ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw; -ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove; \ No newline at end of file diff --git a/static/js/weather.js b/static/js/weather.js index 2521e4ed3..163967f33 100644 --- a/static/js/weather.js +++ b/static/js/weather.js @@ -8,6 +8,9 @@ * @param item weather cell data * @returns {boolean} */ +var L +var markersnotify + function processWeather(i, item) { if (!Store.get('showWeatherCells') || item.gameplay_weather == null) { return false @@ -108,7 +111,7 @@ function deleteObsoleteWeatherAlerts(newAlerts) { */ function safeDelMarker(item) { if (item.marker) { - item.marker.setMap(null) + markersnotify.removeLayer(item.marker) } } @@ -149,15 +152,15 @@ function getWeatherImageUrl(item, dark = true) { function setupWeatherMarker(item) { var imageUrl = getWeatherImageUrl(item) - var image = { - url: imageUrl, - origin: new google.maps.Point(0, 0), - anchor: new google.maps.Point(32, 32) - } - return new google.maps.Marker({ - position: item.center, - icon: image + var image = new L.Icon({ + iconUrl: imageUrl, + iconAnchor: [32, 32], + iconSize: [32, 32] }) + + var weatherMarker = L.marker([item['latitude'], item['longitude']], {icon: image}) + markersnotify.addLayer(weatherMarker) + return weatherMarker } @@ -167,14 +170,15 @@ function setupWeatherMarker(item) { * @returns {google.maps.Polygon} */ function setupS2CellPolygon(item) { - return new google.maps.Polygon({ - paths: item.vertices, - strokeColor: '#000000', - strokeOpacity: 0.8, - strokeWeight: 1, + var s2CellPolygon = L.polygon(item.vertices, { + color: '#000000', + opacity: 0.8, + weight: 1, fillOpacity: 0, fillColor: '#00ff00' }) + markersnotify.addLayer(s2CellPolygon) + return s2CellPolygon } @@ -205,7 +209,7 @@ function createCellAlert(item) { * @returns {google.maps.LatLngBounds} */ function getS2CellBounds(s2Cell) { - var bounds = new google.maps.LatLngBounds() + var bounds = new L.LatLngBounds() // iterate over the vertices $.each(s2Cell.vertices, function (i, latLng) { // extend the bounds @@ -290,10 +294,10 @@ function getMainS2Cell() { var bounds = map.getBounds() var viewportPath = [ - {'lat': bounds.getNorthEast().lat(), 'lng': bounds.getNorthEast().lng()}, - {'lat': bounds.getNorthEast().lat(), 'lng': bounds.getSouthWest().lng()}, - {'lat': bounds.getSouthWest().lat(), 'lng': bounds.getSouthWest().lng()}, - {'lat': bounds.getSouthWest().lat(), 'lng': bounds.getNorthEast().lng()} + {'lat': bounds.getNorthEast().lat, 'lng': bounds.getNorthEast().lng}, + {'lat': bounds.getNorthEast().lat, 'lng': bounds.getSouthWest().lng}, + {'lat': bounds.getSouthWest().lat, 'lng': bounds.getSouthWest().lng}, + {'lat': bounds.getSouthWest().lat, 'lng': bounds.getNorthEast().lng} ] var jstsViewport = createJstsPolygon(geometryFactory, viewportPath) var viewportArea = jstsViewport.getArea() diff --git a/static/sass/layout/_main.scss b/static/sass/layout/_main.scss index 78797c8f6..4dfb88f6b 100644 --- a/static/sass/layout/_main.scss +++ b/static/sass/layout/_main.scss @@ -54,6 +54,7 @@ html, body { cursor: pointer; margin-left: -15px; bottom: 1.5em; + z-index: 1000; &.disabled { opacity: 0.5; } diff --git a/static/sass/layout/_nav.scss b/static/sass/layout/_nav.scss index 9d2fd7fc1..63895fe4b 100644 --- a/static/sass/layout/_nav.scss +++ b/static/sass/layout/_nav.scss @@ -17,7 +17,7 @@ left: 0; visibility: hidden; width: 20.50em; - z-index: 500; + z-index: 1001; h3 { font-size: 18px; font-family: 'Open Sans', sans-serif; @@ -138,4 +138,159 @@ .fa-fw { margin: 0px 5px 0px 0px; } + label[for="notify-perfection"],label[for="notify-level"]{ + h3{ + float:left; + display: -webkit-box; + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + height: 34px; + -webkit-align-items:center; + align-items: center; + margin-bottom: 0; + } + } + label[for="notify-pokemon"]{ + .list .active{ + border: 1px solid #0089ff; + background: rgba(0, 114, 255, 0.4); + } + } + label[for="exclude-pokemon"]{ + .list .active { + border: 1px solid #D3D3D3; + background: rgba(211, 211, 211, 0.4); + } + } + +.list { + align-items: center; + text-align:center; + max-width: 100%; + overflow: auto; + height: 200px; + .pokemon-icon-sprite{ + width: 32%; + margin-bottom: 1px; + margin-left: 3px; + border: 1px solid #D3D3D3; + float: left; + } + } +.pokemon-select-icon { + margin-top: 2px; + + } + +.search { + margin-bottom: 5px; + background-color: #fff; + border: 1px solid #aaa; + padding: 0 10px; + width: 100%; +} + +#pkid_list { + font-size: xx-small; +} + +#pkname_list { + font-size: smaller; +} + +label a { + width: 100%; + float: left; + background: #ffffff; + text-align: center; + text-decoration: none; + border: 1px solid #aaa; + border-radius: 4px; + margin-top: 5px; +} + +#tabs_marker, #tabs_notify{ + padding:0; + border:0; + &:after{ + display: table; + content: ""; + clear: both; + } + .ui-tabs-nav{ + background: transparent; + border: 0; + padding: 0; + display: flex; + li{ + box-sizing: border-box; + margin:0; + flex: 1; + display: flex; + /* If placed in a small width sidebar or something like that disabling nowrap will fix the overflow issue */ + white-space: normal; + border-right:1px solid #fff; + &:last-child{ + border-right-color:#c5c5c5; + } + &:focus{ + + } + a{ + flex: 1; + /* If you want to align tab titles center */ + text-align: center; + padding: 0.5em 0.1em; + text-align: center; + width: 100%; + } + } + } + } +.pokemon-container { + border: 1px solid #c5c5c5; + padding: 1px; + overflow: hidden; + min-height: 300px; +} + +.notify-container{ + border: 1px solid #c5c5c5; + padding: 1px; + overflow: hidden; + min-height: 300px; + padding-top: 10px; +} + +.ui-tabs .ui-tabs-panel { + padding: 0 !important; +} + +.exclude_templates { + max-width: 100%; + overflow: auto; + height: 290px; + .hidepreset { + width: 100%; + margin-bottom: 1px; + border: 1px solid #d3d3d3; + display: table; + } + .hidepreset.active { + background: rgba(211,211,211,.4); + } + .hideicon { + margin: 5px; + float: left; + padding-right: 10px; + } + .hidetext { + float: left; + text-align: center; + display: table; + margin: 10px; + } +} + } diff --git a/static/sass/layout/_stats.scss b/static/sass/layout/_stats.scss index d7f41a171..5b662549d 100644 --- a/static/sass/layout/_stats.scss +++ b/static/sass/layout/_stats.scss @@ -18,7 +18,7 @@ right: 0; visibility: hidden; width: 25em; - z-index: 500; + z-index: 5000; h3 { font-size: 18px; font-family: 'Open Sans', sans-serif; diff --git a/static/sass/statistics.scss b/static/sass/statistics.scss index ff465b81e..5570596d5 100644 --- a/static/sass/statistics.scss +++ b/static/sass/statistics.scss @@ -14,9 +14,20 @@ } #location_map{ + top: 0px; + width: 100%; + bottom: 0px; + height: 100%; +} + +#map { height: 100%; } +.my-leaflet-map-container img { + max-height: none; +} + #times_list{ float: left; background: white; @@ -74,4 +85,4 @@ left: 50%; margin-right: -50%; position: absolute; -} \ No newline at end of file +} diff --git a/templates/map.html b/templates/map.html index cba10db7c..2e817deff 100644 --- a/templates/map.html +++ b/templates/map.html @@ -54,6 +54,7 @@

RocketMap @@ -600,6 +641,14 @@

Location Icon Marker

var generateImages = {{generateImages}}; var rarityFileName = "{{rarityFileName}}"; + + + + + + + + @@ -609,7 +658,16 @@

Location Icon Marker

- + + {% if show.fixed_display %} + + + {% endif %} + diff --git a/templates/map.html.save b/templates/map.html.save new file mode 100644 index 000000000..210fd0090 --- /dev/null +++ b/templates/map.html.save @@ -0,0 +1,667 @@ + + + + + RocketMap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% if show.custom_css %} + + {% endif %} + + +
+ + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + {% if show.custom_js %} + + {% endif %} + + + + + + {% if show.fixed_display %} + + + {% endif %} + + + diff --git a/templates/statistics.html b/templates/statistics.html index 601a1bc17..aa66efa8f 100644 --- a/templates/statistics.html +++ b/templates/statistics.html @@ -94,7 +94,8 @@

Duration

- + +
@@ -116,5 +117,6 @@

Duration

{% endif %} + - \ No newline at end of file +