import { algoliasearch } from 'algoliasearch';
const ALGOLIA_KEY = '9fb3db0222f7b5aef0e2b30791ee6201';
const INDEX_NAME = 'pubfinder';
const client = algoliasearch('YSWWVAX5RB', ALGOLIA_KEY);
const $ = document.querySelector.bind(document);
const algolia_params = {
hitsPerPage: 1000,
};
const dataController = (function () {
const initialRectBounds = {
north: 51.525241,
south: 51.488855,
east: -0.09138,
west: -0.122212,
};
const maxMapSpace = {
north: 51.74,
south: 51.27,
west: -0.51,
east: 0.23,
};
const initialPolygonBounds = [
{ lat: 51.528125, lng: -0.104853 },
{ lat: 51.519405, lng: -0.089005 },
{ lat: 51.508742, lng: -0.108198 },
{ lat: 51.507389, lng: -0.132242 },
{ lat: 51.522659, lng: -0.134998 },
];
const centralPosition = { lat: 51.508616, lng: -0.125319 };
return {
initialRectBounds,
centralPosition,
maxMapSpace,
initialPolygonBounds,
getSearchResults: async function (query = '', shape = '', bounds = []) {
let geoParam;
if (shape === 'rectangle') {
geoParam = {
insideBoundingBox: [bounds],
};
} else if (shape === 'polygon') {
geoParam = {
insidePolygon: [bounds],
};
} else {
geoParam = {
aroundLatLng: bounds,
// aroundRadius: 1000,
};
}
const res = await client.searchSingleIndex({
indexName: INDEX_NAME,
searchParams: {
query,
...algolia_params,
...geoParam,
attributesToRetrieve: ['_geoloc', 'name'],
},
});
return res;
},
getCurrentQuery: function () {
return document.querySelector('#searchbar').value;
},
};
})();
const interfaceController = (function () {
let markers = [];
let shape;
let map;
const DOMStrings = {
selectRect: '.select--rectangle',
selectPoly: '.select--polygon',
ip: '.select--ip',
ipLoader: '.ip-loader',
hits: '#hits',
searchBar: '#searchbar',
};
const generateMap = async function (bounds, position, maxSpace, initialPolygonBounds, currentShape, zoom = 12) {
console.log(bounds, position, maxSpace, initialPolygonBounds, currentShape, zoom);
const { Map, Polygon, Rectangle } = await google.maps.importLibrary('maps');
map = new Map(document.getElementById('map'), {
zoom,
center: position,
mapId: 'pub_map',
restriction: {
latLngBounds: maxSpace,
strictBounds: true,
},
});
if (currentShape === 'rectangle') {
shape = new Rectangle({
bounds: bounds,
editable: true,
draggable: true,
});
shape.setMap(map);
} else if (currentShape === 'polygon') {
shape = new Polygon({
paths: initialPolygonBounds,
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
editable: true,
draggable: true,
geodesic: true,
});
shape.setMap(map);
}
};
return {
handleShapeSelect: function (e) {},
initMap: generateMap,
embedSearchResults: async function (hits) {
const { AdvancedMarkerElement } = await google.maps.importLibrary('marker');
const { InfoWindow } = await google.maps.importLibrary('maps');
hits.forEach((hit) => {
const marker = new AdvancedMarkerElement({
map: map,
position: {
lat: hit._geoloc.lat,
lng: hit._geoloc.lng,
},
});
const window = new InfoWindow({
content: hit.name,
});
marker.addListener('click', function () {
window.open({
anchor: marker,
map,
});
});
markers.push(marker);
});
},
updateHitsList: function (hits) {
const slicedArr = hits.slice(0, 20);
$(DOMStrings.hits).innerHTML = '';
let html = `
`;
for (const pub of slicedArr) {
html += `- ${pub.name}
`;
}
html += `
`;
$(DOMStrings.hits).insertAdjacentHTML('beforeend', html);
},
handleBoundChange: function () {
const { south, west, north, east } = shape.getBounds()?.toJSON();
return [north, east, south, west];
},
getActiveShape: function () {
return shape;
},
getShapeBounds: function () {
if (shape instanceof google.maps.Polygon) {
const paths = shape.getPaths();
let points = [];
paths.forEach((path) => {
path.forEach((latLng) => {
const lat = latLng.lat();
const lng = latLng.lng();
points.push(lat, lng);
});
});
return points;
} else {
const { south, west, north, east } = shape.getBounds()?.toJSON();
return [north, east, south, west];
}
},
removeMarkers: function () {
for (let i = 0; i < markers.length; i++) {
markers[i].setMap(null);
}
},
getDOMStrings: function () {
return DOMStrings;
},
};
})();
const controller = (function (dataCTRL, uiCTRL) {
const DOM = uiCTRL.getDOMStrings();
const rectBounds = dataCTRL.initialRectBounds;
const centralPoint = dataCTRL.centralPosition;
const mapBounds = dataCTRL.maxMapSpace; // This is rectangle bounds
const initialPolygonBounds = dataCTRL.initialPolygonBounds; // Polygon bound
const initialSetup = async function () {
await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle', 12);
const searchResults = await dataCTRL.getSearchResults('', 'rectangle', [
rectBounds.north,
rectBounds.east,
rectBounds.south,
rectBounds.west,
]);
await uiCTRL.embedSearchResults(searchResults.hits);
uiCTRL.updateHitsList(searchResults.hits);
};
const setupMapListeners = async function () {
const shape = uiCTRL.getActiveShape();
if (shape instanceof google.maps.Rectangle) {
let debounceTimer;
shape.addListener('bounds_changed', () => {
uiCTRL.removeMarkers();
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
const newAlgoliaBounds = uiCTRL.handleBoundChange();
const searchResults = await dataCTRL.getSearchResults(
dataCTRL.getCurrentQuery(),
'rectangle',
newAlgoliaBounds
);
await uiCTRL.embedSearchResults(searchResults.hits);
uiCTRL.updateHitsList(searchResults.hits);
}, 200);
});
}
if (shape instanceof google.maps.Polygon) {
let debounceTimer;
const paths = shape.getPaths();
const handlePathDrag = async function () {
uiCTRL.removeMarkers();
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
const points = uiCTRL.getShapeBounds();
const searchResults = await dataCTRL.getSearchResults(dataCTRL.getCurrentQuery(), 'polygon', points);
await uiCTRL.embedSearchResults(searchResults.hits);
uiCTRL.updateHitsList(searchResults.hits);
}, 300);
};
paths.forEach((path) => {
path.addListener('set_at', handlePathDrag);
path.addListener('insert_at', handlePathDrag);
});
}
};
const setupShapeSelectListeners = async function () {
$(DOM.selectRect).addEventListener('click', async () => {
await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle', 12);
const searchResults = await dataCTRL.getSearchResults(dataCTRL.getCurrentQuery(), 'rectangle', [
rectBounds.north,
rectBounds.east,
rectBounds.south,
rectBounds.west,
]);
await uiCTRL.embedSearchResults(searchResults.hits);
uiCTRL.updateHitsList(searchResults.hits);
await setupMapListeners();
});
$(DOM.selectPoly).addEventListener('click', async () => {
await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'polygon', 12);
const translatedPolyBounds = initialPolygonBounds.flatMap(({ lat, lng }) => [lat, lng]);
const searchResults = await dataCTRL.getSearchResults(
dataCTRL.getCurrentQuery(),
'polygon',
translatedPolyBounds
);
await uiCTRL.embedSearchResults(searchResults.hits);
uiCTRL.updateHitsList(searchResults.hits);
await setupMapListeners();
});
$(DOM.ip).addEventListener('click', async () => {
$(DOM.ipLoader).classList.replace('hidden', 'inline-block');
navigator.geolocation.getCurrentPosition(async (pos) => {
const position = { lat: pos.coords.latitude, lng: pos.coords.longitude };
await uiCTRL.initMap('', position, rectBounds, '', 'ip', 10);
const searchResults = await dataCTRL.getSearchResults(
dataCTRL.getCurrentQuery(),
'ip',
`${pos.coords.latitude}, ${pos.coords.longitude}`
);
await uiCTRL.embedSearchResults(searchResults.hits);
uiCTRL.updateHitsList(searchResults.hits);
$(DOM.ipLoader).classList.replace('inline-block', 'hidden');
});
}),
$(DOM.searchBar).addEventListener('input', async (e) => {
const q = dataCTRL.getCurrentQuery();
if (q > 0 && q < 3) return;
const bounds = uiCTRL.getShapeBounds();
const activeShape = uiCTRL.getActiveShape();
const searchResults = await dataCTRL.getSearchResults(
q,
activeShape instanceof google.maps.Polygon ? 'polygon' : 'rectangle',
bounds
);
uiCTRL.removeMarkers();
await uiCTRL.embedSearchResults(searchResults.hits);
uiCTRL.updateHitsList(searchResults.hits);
});
};
return {
init: async function () {
await initialSetup();
await setupMapListeners();
await setupShapeSelectListeners();
},
};
})(dataController, interfaceController);
controller.init();