Source: leaf/Basemap.js

/**
 * @module nyc/leaf/Basemap
 */

import $ from 'jquery'

import nyc from 'nyc'
import leaf from 'nyc/leaf'
import BasemapHelper from 'nyc/BasemapHelper'
import LocalStorage from 'nyc/leaf/LocalStorage'

import L from 'leaflet'

/**
 * @desc Class that provides an L.Map with base layers and labels
 * @public
 * @class
 * @extends L.Map
 * @mixes module:nyc/BasemapHelper~BasemapHelper
 * @see http://leafletjs.com/
 */
class Basemap {
/**
 * @desc Create an instance of Basemap
 * @public
 * @extends {L.Map}
 * @param {module:nyc/leaf/Basemap~Basemap.Options} options Constructor options
 * @constructor
 */
constructor(options) {
		const map = L.map(options.target)
		nyc.mixin(map, [BasemapHelper])
		/**
		 * @private
		 * @member {number}
		 */
		map.latestPhoto = 0
		/**
		 * @private
		 * @member {L.TileLayer}
		 */
		map.base = null
		/**
		 * @private
		 * @member {Object<string, L.TileLayer>}
		 */
		map.labels = {}
		/**
		 * @private
		 * @member {Object<string, L.TileLayer>}
		 */
		map.photos = {}
		/**
		 * @private
		 * @member {storage.Local}
		 */
		map.storage = new LocalStorage()
		this.setupLayers(map, options)
		map.fitBounds(Basemap.EXTENT)
		map.hookupEvents(map.getContainer())
		return map
	}
	/**
	 * @private
	 * @method
	 * @param {L.Map} map
	 * @param {module:nyc/BasemapHelper~BasemapHelper.Options} options
	 */
	setupLayers(map, options) {
		map.base = L.tileLayer(Basemap.BASE_URL, {
			minNativeZoom: 8,
			maxNativeZoom: 21,
			subdomains: '1234',
			tms: true,
			bounds: Basemap.UNIVERSE_EXTENT,
			zIndex: 0
		})
		map.base.name = 'base'
		map.addLayer(map.base)

		Object.entries(Basemap.LABEL_URLS).forEach(([labelType, url]) => {
			map.labels[labelType] = L.tileLayer(Basemap.LABEL_URLS[labelType], {
				minNativeZoom: 8,
				maxNativeZoom: 21,
				subdomains: '1234',
				tms: true,
				bounds: Basemap.LABEL_EXTENT,
				zIndex: 1000
			})
			map.labels[labelType].name = labelType
			if (labelType == 'base') {
				map.addLayer(map.labels[labelType])
			}
		})

		Object.entries(Basemap.PHOTO_URLS).forEach(([year, url]) => {
			const photo = L.tileLayer(url, {
				minNativeZoom: 8,
				maxNativeZoom: 21,
				subdomains: '1234',
				tms: true,
				bounds: Basemap.PHOTO_EXTENT,
				zIndex: 1
			})
			if ((year.split('-')[0] * 1) > map.latestPhoto) {
				map.latestPhoto = year
			}
			photo.name = year
			map.photos[year] = photo
			map.photos[year] = photo
		})
	}
	/**
	 * @desc Get the storage used for loading and saving data
	 * @public
	 * @override
	 * @method
	 * @return {module:nyc/leaf/LocalStorage~LocalStorage} srorage
	 */
	getStorage(year) {
		return this.storage
	}

	/**
	 * @desc Show photo layer
	 * @public
	 * @override
	 * @method
	 * @param year {number=} The photo year to show - shows the latest year if not provided
	 */
	showPhoto(year) {
		year = year || this.latestPhoto
		this.hidePhoto()
		this.addLayer(this.photos[year + ''])
		this.showLabels('photo')
	}
	/**
	 * @desc Show labels by type
	 * @public
	 * @override
	 * @method
	 * @param labelType {module:nyc/BasemapHelper~BasemapHelper} The label type to show
	 */
	showLabels(labelType) {
		this[labelType == BasemapHelper.LabelType.BASE ? 'addLayer' : 'removeLayer'](this.labels.base)
		this[labelType == BasemapHelper.LabelType.PHOTO ? 'addLayer' : 'removeLayer'](this.labels.photo)
	}

	/**
	 * @desc Hide photo layer
	 * @public
	 * @override
	 * @method
	 */
	hidePhoto() {
		this.showLabels(BasemapHelper.LabelType.BASE)
		Object.keys(this.photos).map(photoYear => {
			if (this.hasLayer(this.photos[photoYear])) {
				this.removeLayer(this.photos[photoYear])
			}
		})
	}
	/**
	 * @desc Returns the base layers
	 * @public
	 * @override
	 * @method
	 * @return {module:nyc/BasemapHelper~BasemapHelper.BaseLayers}
	 */
	getBaseLayers() {
		return {
			base: this.base,
			labels: this.labels,
			photos: this.photos
		}
	}
}
/**
 * @desc Constructor options for {@link module:nyc/leaf/Basemap~Basemap}
 * @public
 * @typedef {Object}
 * @property {Element|string} target The target DOM node for creating the map
 */
Basemap.Options;

/**
 * @desc The URL of the New York City base map tiles
 * @private
 * @const
 * @type {string}
 */
Basemap.BASE_URL = `https://${leaf.TILE_HOSTS}/tms/1.0.0/carto/basemap/{z}/{x}/{y}.jpg`

/**
 * @desc The URLs of the New York City aerial imagery map tiles
 * @private
 * @const
 * @type {Object<string, string>}
 */
Basemap.PHOTO_URLS = {
	'1924': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/1924/{z}/{x}/{y}.png8`,
	'1951': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/1951/{z}/{x}/{y}.png8`,
	'1996': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/1996/{z}/{x}/{y}.png8`,
	'2001-2': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/2001-2/{z}/{x}/{y}.png8`,
	'2004': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/2004/{z}/{x}/{y}.png8`,
	'2006': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/2006/{z}/{x}/{y}.png8`,
	'2008': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/2008/{z}/{x}/{y}.png8`,
	'2010': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/2010/{z}/{x}/{y}.png8`,
	'2012': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/2012/{z}/{x}/{y}.png8`,
	'2014': `https://${leaf.TILE_HOSTS}/tms/1.0.0/photo/2014/{z}/{x}/{y}.png8`
}

/**
 * @desc The URLs of the New York City base map label tiles
 * @private
 * @const
 * @type {Object<string, string>}
 */
Basemap.LABEL_URLS = {
	base: `https://${leaf.TILE_HOSTS}/tms/1.0.0/carto/label/{z}/{x}/{y}.png8`,
	photo: `https://${leaf.TILE_HOSTS}/tms/1.0.0/carto/label-lt/{z}/{x}/{y}.png8`
}

/**
 * @private
 * @const
 * @type {L.LatLngBounds}
 */
Basemap.UNIVERSE_EXTENT = L.latLngBounds([39.3682, -75.9374], [42.0329, -71.7187])
/**
 * @desc The bounds of New York City
 * @public
 * @const
 * @type {L.LatLngBounds}
 */
Basemap.EXTENT = L.latLngBounds([40.4931, -74.2594], [40.9181, -73.6958])
/**
 * @desc The center of New York City
 * @public
 * @const
 * @type {L.LatLng}
 */
Basemap.CENTER = L.latLng([40.7033127, -73.979681])
/**
 * @private
 * @const
 * @type {L.LatLngBounds}
 */
Basemap.LABEL_EXTENT = L.latLngBounds([40.0341, -74.2727], [41.2919, -71.9101])
/**
 * @private
 * @const
 * @type {L.LatLngBounds}
 */
Basemap.PHOTO_EXTENT = L.latLngBounds([40.4888, -74.2759], [40.9279, -73.6896])

nyc.inherits(L.Map, Basemap)

export default Basemap