Looking for a way to filter the map markers using pure javascript based upon multiple conditions if they exist. This is based upon having potentially 4 potential filters possible.
So imagine I have two or more field sets
- Project Type (this will be an array of project types ['engineering','construction'])
- Region (this will be an array of regions ['France','Italy'])
- Name (this will be string input 'google')
- Value (integer input '1000000')
I want to filter through the markers and then include those with matching criteria. And exclude the matching criteria if it doesn't exist or have a value set.
Example1: engineering, france, google, 1000000 Example2: construction, italy Example3: engineering, france, 1000000
Trying to achieve this in an elegant manner and not get into a nested if statement pit of hell.
Here is a working example I have setup, which accounts for two filters but isn't as clean as I would like and allow for the inclusion of extra filters easily.
Looking to have it accurately show the information based upon the users selections. So if they choose multiple projects then show them but if they are include a region and a value show where the project type, location and other match.
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
zoom: 5,
center: { lat: -25.77649954803059, lng: 122.03240276153382 },
styles: darkMapStyle,
});
// Define Filter Arrays
let project_typeFilters = [];
let regionFilters = [];
// Reset filter on reload
resetFitlers();
const infoWindow = new google.maps.InfoWindow({
content: "",
disableAutoPan: true,
});
// Add some markers to the map.
for (let i = 0; i < projectData.length; i ) {
let new_marker = new google.maps.Marker({
position: { lat: projectData[i].lat, lng: projectData[i].lng },
title: projectData[i].project_title,
});
// Add marker to map
new_marker.setMap(map);
new_marker.region = projectData[i].region.toLocaleLowerCase();
new_marker.project_type = projectData[i].project_type.toLowerCase();
markers.push(new_marker);
}
console.log(markers);
// Filter Specific
function filterMining() {
for (let i = 0; i < markers.length; i ) {
if (markers[i].project_type == "Mining") {
markers[i].setMap(map);
} else {
markers[i].setMap(null);
}
}
}
// Filter Specific
function filterData() {
//
for (let i = 0; i < markers.length; i ) {
if (project_typeFilters.length > 0 && regionFilters.length > 0) {
// Filter/Show if both are true
if (
project_typeFilters.includes(markers[i].project_type) &&
regionFilters.includes(markers[i].region)
) {
markers[i].setMap(map);
continue;
}
} else if (project_typeFilters.length > 0 || regionFilters.length > 0) {
// Filter/Show for either
if (
project_typeFilters.includes(markers[i].project_type) ||
regionFilters.includes(markers[i].region)
) {
markers[i].setMap(map);
continue;
}
}
if (project_typeFilters.length == 0 && regionFilters.length == 0) {
markers[i].setMap(map);
continue;
}
markers[i].setMap(null);
}
}
// Reset and show all
function showAll() {
resetFitlers();
for (let i = 0; i < markers.length; i ) markers[i].setMap(map);
}
// Reset input and clear filter arrays
function resetFitlers() {
document.querySelectorAll(".filter").forEach((item) => {
item.checked = false;
});
project_typeFilters = [];
regionFilters = [];
}
document.getElementById("reset").onclick = showAll;
document.querySelectorAll(".filter").forEach((item) => {
item.addEventListener("click", () => {
let type = item.getAttribute("data-type");
let value = item.getAttribute("id");
if (
item["checked"] &&
project_typeFilters.includes(value) === false &&
type === "project_type"
) {
project_typeFilters.push(value);
}
if (
item["checked"] &&
regionFilters.includes(value) === false &&
type === "region"
) {
regionFilters.push(value);
}
if (item["checked"] === false && type === "project_type") {
const index = project_typeFilters.findIndex(
(string) => string === value
);
project_typeFilters.splice(index, 1);
}
if (item["checked"] === false && type === "region") {
const index = regionFilters.findIndex((string) => string === value);
regionFilters.splice(index, 1);
}
filterData();
});
});
}
window.initMap = initMap;
Example of projectData JSON
let projectData = [
{
id: 1,
lat: -24.957874041168797,
lng: 118.5013331401608,
label: "Project Name #1",
project_title: "Project #1 Title",
project_url: "https://youtube.com.au",
project_type: "Engineering",
region: "France",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow. Chicken kielbasa boudin, spare ribs doner sausage short loin beef meatloaf tenderloin chislic sirloin ground round pastrami bacon. Meatball meatloaf sausage spare ribs hamburger, kielbasa strip steak ball tip pork belly flank venison. Tongue meatloaf drumstick, sausage alcatra hamburger shankle.",
},
{
id: 2,
lat: -32.83626802206395,
lng: 116.47405764163521,
label: "Project Name #2",
project_title: "Project #2 Title",
project_url: "https://youtube.com.au",
project_type: "Construction",
region: "Italy",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow. Chicken kielbasa boudin, spare ribs doner sausage short loin beef meatloaf tenderloin chislic sirloin ground round pastrami bacon. Meatball meatloaf sausage spare ribs hamburger, kielbasa strip steak ball tip pork belly flank venison. Tongue meatloaf drumstick, sausage alcatra hamburger shankle.",
},
{
id: 3,
lat: -24.957874041168797,
lng: 118.8013331401608,
label: "Project Name #3",
project_title: "Project #3 Title",
project_url: "https://youtube.com.au",
project_type: "Construction",
region: "France",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow. Chicken kielbasa boudin, spare ribs doner sausage short loin beef meatloaf tenderloin chislic sirloin ground round pastrami bacon. Meatball meatloaf sausage spare ribs hamburger, kielbasa strip steak ball tip pork belly flank venison. Tongue meatloaf drumstick, sausage alcatra hamburger shankle.",
},
{
id: 4,
lat: -21.218106010814925,
lng: 119.86911455192619,
label: "Project Name #4",
project_title: "Project #4 Title",
project_url: "https://youtube.com.au",
project_type: "Engineering",
region: "Italy",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow. Chicken kielbasa boudin, spare ribs doner sausage short loin beef meatloaf tenderloin chislic sirloin ground round pastrami bacon. Meatball meatloaf sausage spare ribs hamburger, kielbasa strip steak ball tip pork belly flank venison. Tongue meatloaf drumstick, sausage alcatra hamburger shankle.",
},
{
id: 5,
lat: -21.318106010814925,
lng: 119.86911455192619,
label: "Project Name #5",
project_title: "Project #5 Title",
project_url: "https://youtube.com.au",
project_type: "Engineering",
region: "France",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow. Chicken kielbasa boudin, spare ribs doner sausage short loin beef meatloaf tenderloin chislic sirloin ground round pastrami bacon. Meatball meatloaf sausage spare ribs hamburger, kielbasa strip steak ball tip pork belly flank venison. Tongue meatloaf drumstick, sausage alcatra hamburger shankle.",
},
CodePudding user response:
Given the lack of HTML, the minimal dataset and slightly fuzzy requirements the following might ( or might not ) be the sort of thing you are looking for. I have used this approach before to filter markers on a map where the dataset was much, much larger and the number of criteria was also much larger.
The filtering of markers is handled within the changehandler
function - the comments in the code will help it make sense. You should be able to adapt the logic to checkboxes
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8' />
<title>Google Maps: Filter markers based upon user selected options</title>
<style>
body{
width:100%;height:100vh;margin:0;padding:0;box-sizing:border-box;
}
#map{
width:900px;
height:600px;
float:none;
margin:5rem auto 0;
}
form{
display:flex;
width:100%;
flex-direction:row;
justify-content:center;
margin:1rem auto;
}
select.filter{
padding:0.75rem;
margin:0.25rem;
flex:1
}
p{
width:900px;
margin:auto;
float:none;
}
form *,p{
font-family:monospace
}
</style>
<script>
// utility shortcuts
const q=(e,n=document)=>n.querySelector(e);
const qa=(e,n=document)=>n.querySelectorAll(e);
let projectData = [
{
id: 1,
lat: -24.957874041168797,
lng: 118.5013331401608,
label: "Project Name #1",
project_title: "Project #1 Title",
project_url: "https://youtube.com.au",
project_type: "Engineering",
region: "France",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow",
},
{
id: 2,
lat: -32.83626802206395,
lng: 116.47405764163521,
label: "Project Name #2",
project_title: "Project #2 Title",
project_url: "https://youtube.com.au",
project_type: "Construction",
region: "Italy",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow",
},
{
id: 3,
lat: -24.957874041168797,
lng: 118.8013331401608,
label: "Project Name #3",
project_title: "Project #3 Title",
project_url: "https://youtube.com.au",
project_type: "Construction",
region: "France",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow",
},
{
id: 4,
lat: -21.218106010814925,
lng: 119.86911455192619,
label: "Project Name #4",
project_title: "Project #4 Title",
project_url: "https://youtube.com.au",
project_type: "Engineering",
region: "Italy",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow",
},
{
id: 5,
lat: -21.318106010814925,
lng: 119.86911455192619,
label: "Project Name #5",
project_title: "Project #5 Title",
project_url: "https://youtube.com.au",
project_type: "Engineering",
region: "France",
value: "1000000",
company: "Company Name",
company_url: "https://www.google.com.au",
company_email: "[email protected]",
project_description:
"Bacon ipsum dolor amet kielbasa pastrami beef ribs pig cow",
}
];
function initMap() {
let markers=[];
let Filters={};
const map = new google.maps.Map( q('#map'), {
zoom: 5,
center: { lat: -25.77649954803059, lng: 122.03240276153382 }
});
const infoWindow = new google.maps.InfoWindow({
disableAutoPan: false,
});
const clickhandler=function(e){
/*
open the infowindow and display the properties
from the JSON data for this marker in a very
basic format...
*/
infoWindow.open( map, this );
infoWindow.setPosition( this.position );
infoWindow.setContent( Object.keys( this.data ).map( k=>[ k, this.data[ k ] ].join(': ') ).join('<br />') );
};
// the filter function
const changehandler=(e)=>{
let bounds=new google.maps.LatLngBounds();
/*
If the "select" menu has an option, other than the 1st,
selected we add it to our list of Filters.
*/
if( e.target.selectedIndex==0 && Filters.hasOwnProperty( e.target.name ) ){
delete Filters[ e.target.name ];
}else{
Filters[ e.target.name ]=e.target.value;
}
// hide any open infowindow
infoWindow.close();
// hide ( not remove ) all markers
markers.forEach( mkr=>mkr.setVisible( false ) );
/*
Iterate through all markers and continually
set the boolean value res by validating that the
current Select value matches the appropriate value
from the source data.
*/
let filtered=markers.filter( function( mkr ){
let res=true;
Object.keys( Filters ).forEach( name => {
/* this is the important bit ! */
res = res && Filters[ name ]===mkr.data[ name ];
});
return res;
});
/*
with the returned array of matched markers we
now set their visibility so that they are visible.
*/
filtered.forEach( mkr=>{
mkr.setVisible( true );
bounds.extend( mkr.position );
});
if( !bounds.isEmpty() ) map.fitBounds( bounds );
};
// add the markers...
projectData.forEach(json=>{
let args={
position: { lat:Number( json.lat ), lng:Number( json.lng ) },
title:json.project_title,
data:json,/* This is ALL the json data for this marker - this is used in the filter!! */
map:map
};
let marker=new google.maps.Marker( args );
google.maps.event.addListener( marker, 'click', clickhandler );
markers.push( marker );
});
//assign change event handlers to select menus
qa('.filter').forEach(n=>{
n.addEventListener( 'change', changehandler );
});
}
</script>
</head>
<body>
<div id='map'></div>
<form>
<!--
Note that the names of these "SELECT" menus match the keys
within the JSON source data. This is important with the above
code as the name is used to filter the data.
If the elements cannot be so named then a lookup matrix
would be required which just adds another layer of complexity.
-->
<select name='project_type' class='filter'>
<option selected>Please select "Project Type"
<option>Engineering
<option>Construction
</select>
<select name='project_title' class='filter'>
<option selected>Please select "Project Title"
<option>Project #1 Title
<option>Project #2 Title
<option>Project #3 Title
<option>Project #4 Title
<option>Project #5 Title
</select>
<select name='region' class='filter'>
<option selected>Please select "Region"
<option>France
<option>Italy
</select>
<select name='company_name' class='filter'>
<option selected>Please select "Company Name"
<option>Google
<option>Yahoo
</select>
<select name='value' class='filter'>
<option selected>Please select "Value"
<option>1000000
<option>2000000
</select>
</form>
<script async defer src='//maps.googleapis.com/maps/api/js?key=<APIKEY>&callback=initMap'></script>
</body>
</html>