From 669e1a7edbd0623ae90bee96c22c5a918a758345 Mon Sep 17 00:00:00 2001 From: Kimmo Puputti Date: Thu, 16 Aug 2018 16:31:08 +0300 Subject: [PATCH] Reorganize maps configuration --- .../GeocoderGoogleMaps.js | 32 ++--- .../GeocoderMapbox.js | 27 +---- src/components/Map/DynamicGoogleMap.js | 27 +++-- src/components/Map/DynamicMapboxMap.js | 48 ++++---- src/components/Map/Map.example.js | 18 ++- src/components/Map/Map.js | 27 ++--- src/components/Map/StaticGoogleMap.js | 40 +++---- src/components/Map/StaticMapboxMap.js | 45 ++++---- src/components/SearchMap/SearchMap.js | 12 +- src/config.js | 109 ++++++++++-------- src/containers/ListingPage/SectionMapMaybe.js | 2 +- src/default-location-searches.js | 51 ++++++++ src/util/maps.js | 3 +- 13 files changed, 235 insertions(+), 206 deletions(-) create mode 100644 src/default-location-searches.js diff --git a/src/components/LocationAutocompleteInput/GeocoderGoogleMaps.js b/src/components/LocationAutocompleteInput/GeocoderGoogleMaps.js index 9af86748..b276f9d5 100644 --- a/src/components/LocationAutocompleteInput/GeocoderGoogleMaps.js +++ b/src/components/LocationAutocompleteInput/GeocoderGoogleMaps.js @@ -1,39 +1,21 @@ import React from 'react'; -// import { types as sdkTypes } from '../../util/sdkLoader'; import classNames from 'classnames'; import { getPlacePredictions, getPlaceDetails, locationBounds } from '../../util/googleMaps'; import { userLocation } from '../../util/maps'; +import config from '../../config'; + import css from './LocationAutocompleteInput.css'; -// const { LatLng: SDKLatLng, LatLngBounds: SDKLatLngBounds } = sdkTypes; - export const CURRENT_LOCATION_ID = 'current-location'; -const CURRENT_LOCATION_BOUNDS_DISTANCE = 1000; // meters // A list of default predictions that can be shown when the user // focuses on the autocomplete input without typing a search. This can // be used to reduce typing and Geocoding API calls for common // searches. -export const defaultPredictions = [ - // Examples: - // Current user location from the browser geolocation API - // { - // id: CURRENT_LOCATION_ID, - // predictionPlace: {}, - // }, - // Helsinki - // { - // id: 'default-helsinki', - // predictionPlace: { - // address: 'Helsinki, Finland', - // origin: new SDKLatLng(60.16985, 24.93837), - // bounds: new SDKLatLngBounds( - // new SDKLatLng(60.29783, 25.25448), - // new SDKLatLng(59.92248, 24.78287) - // ), - // }, - // }, -]; +export const defaultPredictions = (config.maps.search.suggestCurrentLocation + ? [{ id: CURRENT_LOCATION_ID, predictionPlace: {} }] + : [] +).concat(config.maps.search.defaults); // When displaying data from the Google Maps Places API, and // attribution is required next to the results. @@ -117,7 +99,7 @@ class GeocoderGoogleMaps { return { address: '', origin: latlng, - bounds: locationBounds(latlng, CURRENT_LOCATION_BOUNDS_DISTANCE), + bounds: locationBounds(latlng, config.maps.search.currentLocationBoundsDistance), }; }); } diff --git a/src/components/LocationAutocompleteInput/GeocoderMapbox.js b/src/components/LocationAutocompleteInput/GeocoderMapbox.js index 2b830026..f4bd6d85 100644 --- a/src/components/LocationAutocompleteInput/GeocoderMapbox.js +++ b/src/components/LocationAutocompleteInput/GeocoderMapbox.js @@ -5,7 +5,6 @@ import config from '../../config'; const { LatLng: SDKLatLng, LatLngBounds: SDKLatLngBounds } = sdkTypes; export const CURRENT_LOCATION_ID = 'current-location'; -const CURRENT_LOCATION_BOUNDS_DISTANCE = 1000; // meters const locationBounds = (latlng, distance) => { const bounds = new window.mapboxgl.LngLat(latlng.lng, latlng.lat).toBounds(distance); @@ -38,26 +37,10 @@ const placeBounds = prediction => { // focuses on the autocomplete input without typing a search. This can // be used to reduce typing and Geocoding API calls for common // searches. -export const defaultPredictions = [ - // Examples: - // Current user location from the browser geolocation API - // { - // id: CURRENT_LOCATION_ID, - // predictionPlace: {}, - // }, - // Helsinki - // { - // id: 'default-helsinki', - // predictionPlace: { - // address: 'Helsinki, Finland', - // origin: new SDKLatLng(60.16985, 24.93837), - // bounds: new SDKLatLngBounds( - // new SDKLatLng(60.29783, 25.25448), - // new SDKLatLng(59.92248, 24.78287) - // ), - // }, - // }, -]; +export const defaultPredictions = (config.maps.search.suggestCurrentLocation + ? [{ id: CURRENT_LOCATION_ID, predictionPlace: {} }] + : [] +).concat(config.maps.search.defaults); export const GeocoderAttribution = () => null; @@ -140,7 +123,7 @@ class GeocoderMapbox { return { address: '', origin: latlng, - bounds: locationBounds(latlng, CURRENT_LOCATION_BOUNDS_DISTANCE), + bounds: locationBounds(latlng, config.maps.search.currentLocationBoundsDistance), }; }); } diff --git a/src/components/Map/DynamicGoogleMap.js b/src/components/Map/DynamicGoogleMap.js index 306a3bc8..ae3f0e31 100644 --- a/src/components/Map/DynamicGoogleMap.js +++ b/src/components/Map/DynamicGoogleMap.js @@ -8,13 +8,13 @@ import config from '../../config'; * It handles some of the google map initialization states. */ const DynamicGoogleMap = withGoogleMap(props => { - const { center, zoom, address, coordinatesConfig } = props; + const { center, zoom, address, mapsConfig } = props; - const { markerURI, anchorX, anchorY, width, height } = coordinatesConfig.customMarker || {}; - const markerIcon = coordinatesConfig.customMarker + const { enabled, url, anchorX, anchorY, width, height } = mapsConfig.customMarker; + const markerIcon = enabled ? { icon: { - url: markerURI, + url, // The origin for this image is (0, 0). origin: new window.google.maps.Point(0, 0), @@ -27,8 +27,15 @@ const DynamicGoogleMap = withGoogleMap(props => { const marker = ; const circleProps = { - options: coordinatesConfig.circleOptions, - radius: coordinatesConfig.coordinateOffset, + options: { + fillColor: mapsConfig.fuzzy.circleColor, + fillOpacity: 0.2, + strokeColor: mapsConfig.fuzzy.circleColor, + strokeOpacity: 0.5, + strokeWeight: 1, + clickable: false, + }, + radius: mapsConfig.fuzzy.offset, center, }; @@ -59,7 +66,7 @@ const DynamicGoogleMap = withGoogleMap(props => { }, }} > - {coordinatesConfig.fuzzy ? circle : marker} + {mapsConfig.fuzzy.enabled ? circle : marker} ); }); @@ -67,8 +74,8 @@ const DynamicGoogleMap = withGoogleMap(props => { DynamicGoogleMap.defaultProps = { address: '', center: null, - zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11, - coordinatesConfig: config.coordinates, + zoom: config.maps.fuzzy.enabled ? config.maps.fuzzy.defaultZoomLevel : 11, + mapsConfig: config.maps, }; DynamicGoogleMap.propTypes = { @@ -78,7 +85,7 @@ DynamicGoogleMap.propTypes = { lng: number.isRequired, }).isRequired, zoom: number, - coordinatesConfig: object, + mapsConfig: object, }; export default DynamicGoogleMap; diff --git a/src/components/Map/DynamicMapboxMap.js b/src/components/Map/DynamicMapboxMap.js index e0fba3b3..38e365fa 100644 --- a/src/components/Map/DynamicMapboxMap.js +++ b/src/components/Map/DynamicMapboxMap.js @@ -4,25 +4,21 @@ import uniqueId from 'lodash/uniqueId'; import { circlePolyline } from '../../util/maps'; import config from '../../config'; -const mapMarker = coordinatesConfig => { - const { customMarker } = coordinatesConfig; - if (customMarker) { +const mapMarker = mapsConfig => { + const { enabled, url, width, height } = mapsConfig.customMarker; + if (enabled) { const element = document.createElement('div'); - element.style.backgroundImage = `url(${customMarker.markerURI})`; - element.style.width = `${customMarker.width}px`; - element.style.height = `${customMarker.height}px`; + element.style.backgroundImage = `url(${url})`; + element.style.width = `${width}px`; + element.style.height = `${height}px`; return new window.mapboxgl.Marker({ element }); } else { return new window.mapboxgl.Marker(); } }; -const circleLayer = (center, coordinatesConfig, layerId) => { - const { fillColor, fillOpacity } = coordinatesConfig.circleOptions; - const path = circlePolyline(center, coordinatesConfig.coordinateOffset).map(([lat, lng]) => [ - lng, - lat, - ]); +const circleLayer = (center, mapsConfig, layerId) => { + const path = circlePolyline(center, mapsConfig.fuzzy.offset).map(([lat, lng]) => [lng, lat]); return { id: layerId, type: 'fill', @@ -37,8 +33,8 @@ const circleLayer = (center, coordinatesConfig, layerId) => { }, }, paint: { - 'fill-color': fillColor, - 'fill-opacity': fillOpacity, + 'fill-color': mapsConfig.fuzzy.circleColor, + 'fill-opacity': 0.2, }, }; }; @@ -59,7 +55,7 @@ class DynamicMapboxMap extends Component { this.updateFuzzyCirclelayer = this.updateFuzzyCirclelayer.bind(this); } componentDidMount() { - const { center, zoom, coordinatesConfig } = this.props; + const { center, zoom, mapsConfig } = this.props; const position = [center.lng, center.lat]; this.map = new window.mapboxgl.Map({ @@ -71,12 +67,12 @@ class DynamicMapboxMap extends Component { }); this.map.addControl(new window.mapboxgl.NavigationControl(), 'top-left'); - if (coordinatesConfig.fuzzy) { + if (mapsConfig.fuzzy.enabled) { this.map.on('load', () => { - this.map.addLayer(circleLayer(center, coordinatesConfig, this.fuzzyLayerId)); + this.map.addLayer(circleLayer(center, mapsConfig, this.fuzzyLayerId)); }); } else { - this.centerMarker = mapMarker(coordinatesConfig); + this.centerMarker = mapMarker(mapsConfig); this.centerMarker.setLngLat(position).addTo(this.map); } } @@ -92,7 +88,7 @@ class DynamicMapboxMap extends Component { return; } - const { center, zoom, coordinatesConfig } = this.props; + const { center, zoom, mapsConfig } = this.props; const { lat, lng } = center; const position = [lng, lat]; @@ -110,7 +106,7 @@ class DynamicMapboxMap extends Component { } // fuzzy circle change - if (coordinatesConfig.fuzzy && centerChanged) { + if (mapsConfig.fuzzy.enabled && centerChanged) { if (this.map.loaded()) { this.updateFuzzyCirclelayer(); } else { @@ -118,14 +114,14 @@ class DynamicMapboxMap extends Component { } } - // NOTE: coordinatesConfig changes are not handled + // NOTE: mapsConfig changes are not handled } updateFuzzyCirclelayer() { if (!this.map) { // map already removed return; } - const { center, coordinatesConfig } = this.props; + const { center, mapsConfig } = this.props; const { lat, lng } = center; const position = [lng, lat]; @@ -133,7 +129,7 @@ class DynamicMapboxMap extends Component { // We have to use a different layer id to avoid Mapbox errors this.fuzzyLayerId = generateFuzzyLayerId(); - this.map.addLayer(circleLayer(center, coordinatesConfig, this.fuzzyLayerId)); + this.map.addLayer(circleLayer(center, mapsConfig, this.fuzzyLayerId)); this.map.setCenter(position); } @@ -150,8 +146,8 @@ class DynamicMapboxMap extends Component { DynamicMapboxMap.defaultProps = { address: '', center: null, - zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11, - coordinatesConfig: config.coordinates, + zoom: config.maps.fuzzy.enabled ? config.maps.fuzzy.defaultZoomLevel : 11, + mapsConfig: config.maps, }; DynamicMapboxMap.propTypes = { @@ -161,7 +157,7 @@ DynamicMapboxMap.propTypes = { lng: number.isRequired, }).isRequired, zoom: number, - coordinatesConfig: object, + mapsConfig: object, }; export default DynamicMapboxMap; diff --git a/src/components/Map/Map.example.js b/src/components/Map/Map.example.js index 82c155e5..f2d24e74 100644 --- a/src/components/Map/Map.example.js +++ b/src/components/Map/Map.example.js @@ -31,9 +31,12 @@ export const WithObfuscatedLocation = { obfuscatedCenter: new LatLng(60.16502999999999, 24.940064399999983), address: 'Sharetribe', zoom: 14, - coordinatesConfig: { - ...config.coordinates, - fuzzy: true, + mapsConfig: { + ...config.maps, + fuzzy: { + ...config.maps.fuzzy, + enabled: true, + }, }, }, }; @@ -48,9 +51,12 @@ export const WithCircleLocation = { center: new LatLng(60.16502999999999, 24.940064399999983), obfuscatedCenter: obfuscatedCoordinates(new LatLng(60.16502999999999, 24.940064399999983)), address: 'Sharetribe', - coordinatesConfig: { - ...config.coordinates, - fuzzy: true, + mapsConfig: { + ...config.maps, + fuzzy: { + ...config.maps.fuzzy, + enabled: true, + }, }, }, }; diff --git a/src/components/Map/Map.js b/src/components/Map/Map.js index e769252d..91012122 100644 --- a/src/components/Map/Map.js +++ b/src/components/Map/Map.js @@ -18,32 +18,27 @@ export class Map extends Component { center, obfuscatedCenter, zoom, - coordinatesConfig, + mapsConfig, useStaticMap, } = this.props; const classes = classNames(rootClassName || css.root, className); const mapClasses = mapRootClassName || css.mapRoot; - if (coordinatesConfig.fuzzy && !obfuscatedCenter) { + if (mapsConfig.fuzzy.enabled && !obfuscatedCenter) { throw new Error( - 'Map: obfuscatedCenter prop is required when config.coordinates.fuzzy === true' + 'Map: obfuscatedCenter prop is required when config.maps.fuzzy.enabled === true' ); } - if (!coordinatesConfig.fuzzy && !center) { - throw new Error('Map: center prop is required when config.coordinates.fuzzy === false'); + if (!mapsConfig.fuzzy.enabled && !center) { + throw new Error('Map: center prop is required when config.maps.fuzzy.enabled === false'); } - const location = coordinatesConfig.fuzzy ? obfuscatedCenter : center; + const location = mapsConfig.fuzzy.enabled ? obfuscatedCenter : center; return !isMapsLibLoaded() ? (
) : useStaticMap ? ( - + ) : ( } @@ -53,7 +48,7 @@ export class Map extends Component { center={location} zoom={zoom} address={address} - coordinatesConfig={coordinatesConfig} + mapsConfig={mapsConfig} /> ); } @@ -64,8 +59,8 @@ Map.defaultProps = { rootClassName: null, mapRootClassName: null, address: '', - zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11, - coordinatesConfig: config.coordinates, + zoom: config.maps.fuzzy.enabled ? config.maps.fuzzy.defaultZoomLevel : 11, + mapsConfig: config.maps, useStaticMap: false, }; @@ -77,7 +72,7 @@ Map.propTypes = { center: propTypes.latlng, obfuscatedCenter: propTypes.latlng, zoom: number, - coordinatesConfig: object, + mapsConfig: object, useStaticMap: bool, }; diff --git a/src/components/Map/StaticGoogleMap.js b/src/components/Map/StaticGoogleMap.js index a35285e2..3be7576c 100644 --- a/src/components/Map/StaticGoogleMap.js +++ b/src/components/Map/StaticGoogleMap.js @@ -34,21 +34,17 @@ const convertOpacity = opacity => { }; // Draw a circle polyline for fuzzy location. -const drawFuzzyCircle = (coordinatesConfig, center) => { - if ( - !( - coordinatesConfig && - typeof coordinatesConfig === 'object' && - center && - typeof center === 'object' - ) - ) { +const drawFuzzyCircle = (mapsConfig, center) => { + if (!(mapsConfig && typeof mapsConfig === 'object' && center && typeof center === 'object')) { return ''; } - const { fillColor, fillOpacity, strokeColor, strokeWeight } = coordinatesConfig.circleOptions; + const fillColor = mapsConfig.fuzzy.circleColor; + const fillOpacity = 0.2; + const strokeColor = mapsConfig.fuzzy.circleColor; + const strokeWeight = 1; - const circleRadius = coordinatesConfig.coordinateOffset || 500; + const circleRadius = mapsConfig.fuzzy.offset || 500; const circleStrokeWeight = strokeWeight || 1; const circleStrokeColor = formatColorFromString(strokeColor); const circleStrokeOpacity = convertOpacity(DEFAULT_STROKE_OPACITY); @@ -70,8 +66,8 @@ const drawFuzzyCircle = (coordinatesConfig, center) => { // Get custom marker data for static map URI const customMarker = (options, lat, lng) => { - const { anchorX, anchorY, markerURI } = options; - return [`anchor:${anchorX},${anchorY}`, `icon:${markerURI}`, `${lat},${lng}`].join('|'); + const { anchorX, anchorY, url } = options; + return [`anchor:${anchorX},${anchorY}`, `icon:${url}`, `${lat},${lng}`].join('|'); }; class StaticGoogleMap extends Component { @@ -84,7 +80,7 @@ class StaticGoogleMap extends Component { } render() { - const { center, zoom, address, coordinatesConfig, dimensions } = this.props; + const { center, zoom, address, mapsConfig, dimensions } = this.props; const { lat, lng } = center || {}; const { width, height } = dimensions; @@ -92,10 +88,10 @@ class StaticGoogleMap extends Component { // 1. if fuzzy coordinates are used, return circle path // 2. if customMarker is defined in config.js, use that // 3. else return default marker - const targetMaybe = coordinatesConfig.fuzzy - ? { path: drawFuzzyCircle(coordinatesConfig, center) } - : coordinatesConfig.customMarker - ? { markers: customMarker(coordinatesConfig.customMarker, lat, lng) } + const targetMaybe = mapsConfig.fuzzy.enabled + ? { path: drawFuzzyCircle(mapsConfig, center) } + : mapsConfig.customMarker.enabled + ? { markers: customMarker(mapsConfig.customMarker, lat, lng) } : { markers: `${lat},${lng}` }; const srcParams = stringify({ @@ -103,7 +99,7 @@ class StaticGoogleMap extends Component { zoom, size: `${width}x${height}`, maptype: 'roadmap', - key: config.googleMapsAPIKey, + key: config.maps.googleMapsAPIKey, ...targetMaybe, }); @@ -118,8 +114,8 @@ StaticGoogleMap.defaultProps = { rootClassName: null, address: '', center: null, - zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11, - coordinatesConfig: config.coordinates, + zoom: config.maps.fuzzy.enabled ? config.maps.fuzzy.defaultZoomLevel : 11, + mapsConfig: config.maps, }; StaticGoogleMap.propTypes = { @@ -131,7 +127,7 @@ StaticGoogleMap.propTypes = { lng: number.isRequired, }).isRequired, zoom: number, - coordinatesConfig: object, + mapsConfig: object, // from withDimensions dimensions: shape({ diff --git a/src/components/Map/StaticMapboxMap.js b/src/components/Map/StaticMapboxMap.js index 2860e1e0..695e682e 100644 --- a/src/components/Map/StaticMapboxMap.js +++ b/src/components/Map/StaticMapboxMap.js @@ -9,42 +9,41 @@ const formatColor = color => { return color.replace(/^#/, ''); }; -const fuzzyCircleOverlay = (center, coordinatesConfig) => { - const { - strokeWeight, - strokeColor, - strokeOpacity, - fillColor, - fillOpacity, - } = coordinatesConfig.circleOptions; - const path = circlePolyline(center, coordinatesConfig.coordinateOffset); +const fuzzyCircleOverlay = (center, mapsConfig) => { + const strokeWeight = 1; + const strokeColor = mapsConfig.fuzzy.circleColor; + const strokeOpacity = 0.5; + const fillColor = mapsConfig.fuzzy.circleColor; + const fillOpacity = 0.2; + + const path = circlePolyline(center, mapsConfig.fuzzy.offset); const styles = `-${strokeWeight}+${formatColor(strokeColor)}-${strokeOpacity}+${formatColor( fillColor )}-${fillOpacity}`; return `path${styles}(${encodeURIComponent(polyline.encode(path))})`; }; -const customMarkerOverlay = (center, coordinatesConfig) => { - const { markerURI } = coordinatesConfig.customMarker; - return `url-${encodeURIComponent(markerURI)}(${center.lng},${center.lat})`; +const customMarkerOverlay = (center, mapsConfig) => { + const { url } = mapsConfig.customMarker; + return `url-${encodeURIComponent(url)}(${center.lng},${center.lat})`; }; const markerOverlay = center => { return `pin-s(${center.lng},${center.lat})`; }; -const mapOverlay = (center, coordinatesConfig) => { - if (coordinatesConfig.fuzzy) { - return fuzzyCircleOverlay(center, coordinatesConfig); +const mapOverlay = (center, mapsConfig) => { + if (mapsConfig.fuzzy.enabled) { + return fuzzyCircleOverlay(center, mapsConfig); } - if (coordinatesConfig.customMarker) { - return customMarkerOverlay(center, coordinatesConfig); + if (mapsConfig.customMarker.enabled) { + return customMarkerOverlay(center, mapsConfig); } return markerOverlay(center); }; const StaticMapboxMap = props => { - const { address, center, zoom, coordinatesConfig, dimensions } = props; + const { address, center, zoom, mapsConfig, dimensions } = props; const { width, height } = dimensions; const libLoaded = typeof window !== 'undefined' && window.mapboxgl; @@ -52,13 +51,13 @@ const StaticMapboxMap = props => { return null; } - const overlay = mapOverlay(center, coordinatesConfig); + const overlay = mapOverlay(center, mapsConfig); const src = 'https://api.mapbox.com/styles/v1/mapbox/streets-v10/static' + (overlay ? `/${overlay}` : '') + `/${center.lng},${center.lat},${zoom}` + `/${width}x${height}` + - `?access_token=${window.mapboxgl.accessToken}`; + `?access_token=${config.maps.mapboxAccessToken}`; return {address}; }; @@ -66,8 +65,8 @@ const StaticMapboxMap = props => { StaticMapboxMap.defaultProps = { address: '', center: null, - zoom: config.coordinates.fuzzy ? config.coordinates.fuzzyDefaultZoomLevel : 11, - coordinatesConfig: config.coordinates, + zoom: config.maps.fuzzy.enabled ? config.maps.fuzzy.defaultZoomLevel : 11, + mapsConfig: config.maps, }; StaticMapboxMap.propTypes = { @@ -77,7 +76,7 @@ StaticMapboxMap.propTypes = { lng: number.isRequired, }).isRequired, zoom: number, - coordinatesConfig: object, + mapsConfig: object, // from withDimensions dimensions: shape({ diff --git a/src/components/SearchMap/SearchMap.js b/src/components/SearchMap/SearchMap.js index 66515b99..db100728 100644 --- a/src/components/SearchMap/SearchMap.js +++ b/src/components/SearchMap/SearchMap.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { arrayOf, bool, func, number, string, shape } from 'prop-types'; +import { arrayOf, bool, func, number, string, shape, object } from 'prop-types'; import { withRouter } from 'react-router-dom'; import { withGoogleMap, GoogleMap } from 'react-google-maps'; import classNames from 'classnames'; @@ -279,14 +279,14 @@ export class SearchMapComponent extends Component { onCloseAsModal, onIdle, zoom, - coordinatesConfig, + mapsConfig, activeListingId, } = this.props; const classes = classNames(rootClassName || css.root, className); const mapClasses = mapRootClassName || css.mapRoot; const listingsWithLocation = originalListings.filter(l => !!l.attributes.geolocation); - const listings = coordinatesConfig.fuzzy + const listings = mapsConfig.fuzzy.enabled ? withCoordinatesObfuscated(listingsWithLocation) : listingsWithLocation; const infoCardOpen = this.state.infoCardOpen; @@ -345,7 +345,7 @@ SearchMapComponent.defaultProps = { onCloseAsModal: null, useLocationSearchBounds: true, zoom: 11, - coordinatesConfig: config.coordinates, + mapsConfig: config.maps, }; SearchMapComponent.propTypes = { @@ -361,9 +361,7 @@ SearchMapComponent.propTypes = { onIdle: func.isRequired, useLocationSearchBounds: bool, // eslint-disable-line react/no-unused-prop-types zoom: number, - coordinatesConfig: shape({ - fuzzy: bool.isRequired, - }), + mapsConfig: object, // from withRouter history: shape({ diff --git a/src/config.js b/src/config.js index 054e7c14..fcee6962 100644 --- a/src/config.js +++ b/src/config.js @@ -1,4 +1,5 @@ import * as custom from './marketplace-custom-config.js'; +import defaultLocationSearches from './default-location-searches'; const env = process.env.REACT_APP_ENV; const dev = process.env.REACT_APP_ENV === 'development'; @@ -262,57 +263,74 @@ const siteFacebookPage = 'https://www.facebook.com/Sharetribe/'; // You should create one to track social sharing in Facebook const facebookAppId = null; -// Google Maps API key is needed for static map images. -const googleMapsAPIKey = process.env.REACT_APP_GOOGLE_MAPS_API_KEY; +const maps = { + mapboxAccessToken: process.env.REACT_APP_MAPBOX_ACCESS_TOKEN, + googleMapsAPIKey: process.env.REACT_APP_GOOGLE_MAPS_API_KEY, -const coordinates = { - // If true, obfuscate the coordinates of the listings that are shown - // on a map. - fuzzy: false, + // The location search input can be configured to show default + // searches when the user focuses on the input and hasn't yet typed + // anything. This reduces typing and avoids too many Geolocation API + // calls for common searches. + search: { + // When enabled, the first suggestion is "Current location" that + // uses the browser Geolocation API to query the user's current + // location. + suggestCurrentLocation: process.env.REACT_APP_DEFAULT_SEARCHES_ENABLED === 'true', - // Default zoom level when showing a single circle on a Map. Should - // be small enough so the whole circle fits in. - fuzzyDefaultZoomLevel: 14, + // Distance in meters for calculating the bounding box around the + // current location. + currentLocationBoundsDistance: 1000, - // When fuzzy === true, the coordinates on the Map component are - // obfuscated and a circle is shown instead of a marker. To decide a - // proper value for the offset and the radius, see: - // - // https://gis.stackexchange.com/a/8674 - - // Amount of maximum offset in meters that is applied to obfuscate - // the original coordinates. The actual value is random, but the - // obfuscated coordinates are withing a circle that has the same - // radius as the offset. - coordinateOffset: 500, - - // Options to style the circle appearance. - // - // See: https://developers.google.com/maps/documentation/javascript/reference#CircleOptions - circleOptions: { - fillColor: '#c0392b', - fillOpacity: 0.2, - strokeColor: '#c0392b', - strokeOpacity: 0.5, - strokeWeight: 1, - clickable: false, + // Example location can be edited in the + // `default-location-searches.js` file. + defaults: defaultLocationSearches, }, - // An option to use custom marker on static maps. Uncomment to enable it. - // https://developers.google.com/maps/documentation/maps-static/dev-guide#Markers + // When fuzzy locations are enabled, coordinates on maps are + // obfuscated randomly around the actual location. // - // Note 1: fuzzy coordinate circle overwrites these custom marker settings) - // Note 2: markerURI needs to be a public URI accessible by Google Maps API servers. - // The easiest place is /public/static/icons/ folder, but then the marker image is not available - // while developing through localhost. + // NOTE: This only hides the locations in the UI level, the actual + // coordinates are still accessible in the HTTP requests and the + // Redux store. + fuzzy: { + enabled: false, - // customMarker: { - // markerURI: encodeURI(`${canonicalRootURL}/static/icons/map-marker-32x32.png`), - // anchorX: 16, - // anchorY: 32, - // width: 32, - // height: 32, - // }, + // Amount of maximum offset in meters that is applied to obfuscate + // the original coordinates. The actual value is random, but the + // obfuscated coordinates are withing a circle that has the same + // radius as the offset. + offset: 500, + + // Default zoom level when showing a single circle on a Map. Should + // be small enough so the whole circle fits in. + defaultZoomLevel: 13, + + // Color of the circle on the Map component. + circleColor: '#c0392b', + }, + + // Custom marker image to use in the Map component. + // + // NOTE: Not used if fuzzy locations are enabled. + customMarker: { + enabled: false, + + // Publicly accessible URL for the custom marker image. + // + // The easiest place is /public/static/icons/ folder, but then the + // marker image is not available while developing through + // localhost. + url: encodeURI(`${canonicalRootURL}/static/icons/map-marker-32x32.png`), + + // Dimensions of the marker image. + width: 32, + height: 32, + + // Position to anchor the image in relation to the coordinates, + // ignored when using Mapbox. + anchorX: 16, + anchorY: 32, + }, }; // NOTE: only expose configuration that should be visible in the @@ -350,8 +368,7 @@ const config = { facebookAppId, sentryDsn, usingSSL, - googleMapsAPIKey, - coordinates, + maps, custom, }; diff --git a/src/containers/ListingPage/SectionMapMaybe.js b/src/containers/ListingPage/SectionMapMaybe.js index 521d5ff9..a6d0127c 100644 --- a/src/containers/ListingPage/SectionMapMaybe.js +++ b/src/containers/ListingPage/SectionMapMaybe.js @@ -25,7 +25,7 @@ class SectionMapMaybe extends Component { const address = publicData.location ? publicData.location.address : ''; const classes = classNames(rootClassName || css.sectionMap, className); - const mapProps = config.coordinates.fuzzy + const mapProps = config.maps.fuzzy.enabled ? { obfuscatedCenter: obfuscatedCoordinates(geolocation, listingId ? listingId.uuid : null) } : { address, center: geolocation }; const map = ; diff --git a/src/default-location-searches.js b/src/default-location-searches.js new file mode 100644 index 00000000..e83c37ba --- /dev/null +++ b/src/default-location-searches.js @@ -0,0 +1,51 @@ +import { types as sdkTypes } from './util/sdkLoader'; + +const { LatLng, LatLngBounds } = sdkTypes; + +// An array of locations to show in the LocationAutocompleteInput when +// the input is in focus but the user hasn't typed in any search yet. +// +// Each item in the array should be an object with a unique `id` (String) and a +// `predictionPlace` (util.types.place) properties. +export default [ + { + id: 'default-helsinki', + predictionPlace: { + address: 'Helsinki, Finland', + origin: new LatLng(60.16985, 24.93837), + bounds: new LatLngBounds(new LatLng(60.29783, 25.25448), new LatLng(59.92248, 24.78287)), + }, + }, + { + id: 'default-turku', + predictionPlace: { + address: 'Turku, Finland', + origin: new LatLng(60.45181, 22.26663), + bounds: new LatLngBounds(new LatLng(60.53045, 22.38197), new LatLng(60.33361, 22.06644)), + }, + }, + { + id: 'default-tampere', + predictionPlace: { + address: 'Tampere, Finland', + origin: new LatLng(61.49775, 23.76095), + bounds: new LatLngBounds(new LatLng(61.83657, 24.11838), new LatLng(61.42728, 23.5422)), + }, + }, + { + id: 'default-oulu', + predictionPlace: { + address: 'Oulu, Finland', + origin: new LatLng(65.01208, 25.46507), + bounds: new LatLngBounds(new LatLng(65.56434, 26.77069), new LatLng(64.8443, 24.11494)), + }, + }, + { + id: 'default-ruka', + predictionPlace: { + address: 'Ruka, Finland', + origin: new LatLng(66.16546, 29.15172), + bounds: new LatLngBounds(new LatLng(66.16997, 29.16773), new LatLng(66.16095, 29.13572)), + }, + }, +]; diff --git a/src/util/maps.js b/src/util/maps.js index efcf4672..b3f410e9 100644 --- a/src/util/maps.js +++ b/src/util/maps.js @@ -12,10 +12,9 @@ const { LatLng } = sdkTypes; */ const obfuscatedCoordinatesImpl = (latlng, cacheKey) => { const { lat, lng } = latlng; - const offset = config.coordinates.coordinateOffset; // https://gis.stackexchange.com/questions/25877/generating-random-locations-nearby - const r = offset / 111300; + const r = config.maps.fuzzy.offset / 111300; const y0 = lat; const x0 = lng;