Clustering data in Google Maps and React

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hello hello hello I'm Lee Holliday and this is what we're building today this map here you may be thinking it's just Google Maps no it's something we're going to build it's in react and it's bringing remote data from an API and then we're clustering that data and then we're going to allow you to click into the markers and it will zoom you in to the next level when it breaks those clusters apart so those are the four things we're building today the data it's coming from the UK police department they've got an API where you can get all the crimes for a date and all the crimes have latitude and longitude which is perfect because that's what's needed to plot them on a map so to get started I've got a crate react app that has a few packages installed already I'll just cover those quickly we've got SWR that's for fetching remote data using hooks it's a package from the folks at site the people behind next J s in the the site now serverless platform we've got Google map react if you've watched my other Google Maps video this is a different package I'm trying out it seems a little bit easier to set up so that's what we're going to use and I've got a package called used supercluster it's actually one that I made and it basically allows you to use supercluster pretty easily inside of react so we've got a component called app and I've sort of already divided up the the different sections we're gonna cover we've got map setup loading and formatting the data getting the clusters and then actually rendering the map so the last thing is any component just returning what it should render so right now we're rendering a div I've already set it up to basically span the entire viewport so we've got a hundred percent VH for height and a hundred percent width we could do V W for width but percentage works was width and it's just saying map right now which is obviously not what we want so let's get started inside of the div we'll be putting this google map react so just like that and inside of here is where we'll be placing the markers that we will be rendering out so we need to pass a number of props to the Google map react components so that it works first and foremost being bootstrap URL keys Google Maps doesn't work without URL keys so you need to go into the Google Maps API console and enable Maps or JavaScript Maps or sort of I forget what it's called but whatever you would typically use with Google Maps I'll post it in the description below cuz I can't remember it so I've got a key that I'm going to be passing in and the key for me is living in this dot and dot local file I won't show it so that I won't expose my keys it's a security concern but it is um what is it process dot N and it is react app Google key so I think even if I just do this just with the key I'm not getting anything yet so let's keep going so the next thing to pass in is the default Center so this is where the map is going to be displayed on the screen and we need to pass in a latitude and a longitude so I will grab that and I'll just basically use the first latitude and longitude from this antisocial behavior that happened and we'll so we'll paste that in for lat Google uses LNG not ello n for longitude so we'll paste in the longitude it's the map showing yet nope those keep going so the next thing we want is the default zoom how far should the map be zoomed in when it loads so we'll try 10 and that's what we needed to get the map going so you need those three pieces of information you need the API key that you should be storing in a an environment variable you've got the center of the map where it's positioned and how far it zoomed in with the default zoom so I'd say now that we've got the map displaying let's start up here at the top so map set up there's a few variables in state that I need to basically complete this demo the first one that I need is a ref to the map itself so we'll call this the map ref very original name and we'll use the use ref hook that I've already imported up here from from react and just like that the next thing we need is a little bit of state that we are going to use when we're getting the clusters down here but we'll just set it up now so what we need is the current zoom of the map so we'll set up zoom and set zoom to change that and now we'll be using the use state hook so because I know that I'm setting my map at the beginning to 10 I'll just set this one to 10 as well and we also need the bounds of the map so that are basically the corners so up here here here here the latitude and longitude so that the clustering tool sort of knows what section of the map were using and we're displaying to the user so this will also be used State and for this I'll just set it up to be null at the beginning and then we'll eventually fill that in with those variables set up why don't we start loading the data and then we'll just try to get the marker showing up on a map first and then we'll convert those markers into clusters so I've already imported use SWR and the way you use that is you call use the SWR and i have a full video on this a link to it below if your intro interested in a more thorough intro to this hook but what you need to pass in is a key and in our case that is a URL so I'm gonna put URL there and then create a variable for it so we'll come over here to the browser we'll copy and paste our URL and that is being passed in here the next thing you need to pass into the use SWR function is what they call a fetcher so we'll say fetcher and what a fetcher is it's a function and so it's a function that will receive whatever you passed into this key parameter here his key argument and its purpose is basically to go and fetch the data and return with our promise that will eventually resolve to the actual data from that request so we will receive sort of whatever arguments are passed in we know it's a URL but this way it's a little bit more generic and it needs to return a promise so we're going to work with the fetch library that already returns a promise so we'll pass in all the same args which will be the URL and with fetch you actually deal with two promises so the first one gives you something that resolves to a response and the response then returns a promise that resolves to JSON it's a JSON version of that response I've always sort of sort of found it weird that it is like this but that's what it's like so what does use SWR return it returns us an object that we can D structure so we're grabbing data and an error so data will be the actual data and error is if there was an error on the request so what we're going to do is we're going to basically look at these two variables and from this grab the crimes so the the way we know whether there's a crime or not is if there's data because when it's loading data is empty I think it's undefined or null one of those two which is fall C either way and there is not an error that means we have crime data so if those are true we're gonna grab the data but I'm actually just gonna slice out the first 200 crimes and the reason you do clustering at all is when you're dealing with too much data the Mapp starts to struggle so if we show like 1500 markers it's gonna start to slow down so that's why we cluster them together in groups so we can say in this little group here there's two hundred crimes or three hundred crimes or whatever it is so if there's no data or there is an error we're just going to return an empty array so that it doesn't fail out and now that we have these crimes we can actually come down here and start to render out the markers later on we'll convert those into clusters but for now let's just get something on the screen so we're gonna map the crimes so crimes dot map and each of those will give us a crime and what we want to return is a marker so with this library here we're actually going to create our own marker component and it's gonna be sort of weird the only thing we really care about is children and rendering out the children so it's sort of a component that really does nothing it just it has a name of marker and it returns its children but this library from what I can tell requires you to have your own sort of marker component because - this marker component is where we attach the latitude and longitude of where to display it so now that we have our marker component set up we can first of all we can attach a key because anytime you map you need to give that thing a key so react can differentiate between different markers so each crime has an ID so we'll use that we'll give it the lat so crime I believe it's dot location dot latitude and then LNG crime dot oh boy crime dot location dot longitude and inside our marker we can put whatever HTML we want so I'm gonna put I'll put a div actually now let's put a button and our button will have a class of I set something up crime marker just like that and inside the button we will put an image and it will point to this this thing I have here called custody you'll see what it looks like in a sec custody dot SVG will give it an alt so it's in it it's accessible and we'll call it crime doesn't pay because it definitely does not um and let's see what it's looking like cool so it's already it's already showing up and all those hands if we zoom in we can see how they're sort of where they're located and there are little buttons here right now they don't do anything when when you click but that would be up to you to implement an on click event it's working okay with 200 markers but let's see if we got that up to 2,000 so we reload the page and my computer is struggling sort of I tried to zoom in waiting for it to zoom waiting for it to zoom it barely like I'm trying to drag it around it's not working uh-huh I doubt you can hear my fan but my computer fans going crazy as well so even with sort of I think there's about 1500 markers here it's freaking out it can't handle it part of that I think has to do with using Google Maps within react but we can solve that by implementing clustering so we're back to 200 just so that the map works without too much difficulty so we have the crimes being shown as markers but it's time to go up here and place them into clusters and for that to work we're going to use the used supercluster hook that i've imported and what it returns us our clusters so use supercluster and this hook requires us to pass in a number of pieces of data the first are points it's basically the crimes but we've converted them into a special format that it's expecting it's like a Geo it has an official name here in this article I wrote it's a Geo JSON feature object we need to pass in some bounds that we've already set up here in a variable but it doesn't have a value yet we need to pass the current zoom level of the map so we'll pass in zoom which is also up here in state and then we can pass in some options that are options that go straight to super cluster itself so radius how big are the zones of clustering that it should use and the max zoom we allow our map to go to we'll just say 20 so you can see we don't have points yet it's giving me an error so now it's time where we convert the crime's into the proper points format so we'll say Kant's points equals and we'll map through all of the crimes so each crime will be converted into a point and I'm going to go back to my article and just copy the format that I need so that I don't have to memorize that and paste that in here so the way it looks like it's an object that has a type of feature it's got a properties object which is sort of any key value pairings that you want so we will we've got cluster false and I'll show why that's important in a second I'm gonna pass down the crime ID so that would be crime dydy I've got the category I'm not really using this here but it's just a show that you can pass in any additional information that you want so that would be crime dot category and we have to pass in a geometry object that's a type point and it has some coordinates so I think the order that they're in is longitude first and then latitude so we need to come in here and replace this with crime dot location dot longitude and you you look at this data it's actually giving us the longitude as a string we want it as a number as a float so we're going to come back here and we're going to parse this as a float so that we have it in the right format so we'll parse let me just get rid of that so we'll parsefloat this as well and this will be crime that location dot latitude cool so what we've done is we've taken the crimes and their current format that they come in as we've mapped them and mapping goes through each element and gives us a new array of the the new format so we're returning a new object for each of them that that is this geo geo JSON feature object so now we have an array of those and we're able to pass those into supercluster so now that supercluster will give us back clusters but I think before we move on and start showing these clusters we need to first get the bounds in the zoom updating as the user moves the map around and to get that to work there's a few additional props we have to add on to our google map react the first thing we need to do is add on this super insane prop called yes I want to use Google map API internals it's crazy and I think they made it that way on purpose because I guess unless you actually need to access the the instance of the Google map itself um they don't really want that exposed so you have to add on this insane prop and then what we can do is we can use another prop called on Google API loaded so this is a an event that happens and so we'll pass in a function and it gives us an object that we can extract the actual Google Map instance out of so with this Google Map instance we can place it up in the ref that we created up here at the beginning map ref so map raff current and that is equal to map so now that we have a reference to the actual Google map itself another thing we can do is listen to when the map changes so on change is another event and this event gives us access to two things it's the current zoom of the map and the bounds of the map as well so with these two pieces of information this function will be called every time the user sort of moves the map around or they zoom in or what not so we're gonna take the zoom and plot a pass it to our set zoom function like that and we will take the bounds but instead of just passing it in we actually need to pass the bounds as an array and I wrote down a piece of paper because I can never remember the order they go in so bounds we're gonna start with the North West Point and grab the longitude then we're gonna go to the south east point grab the latitude then we're gonna go back to the southeast point but grab the longitude again and then we're going to grab go back to the northwest and will grab the latitude um there's documentation on both the use supercluster package but also on the supercluster package itself of the order that these have to go in if you just put them in the wrong order it's not gonna blow up you just won't see clusters correctly so you'll you'll notice that it's it's not working right so now that we have the unchanged event here every time the user moves we're updating our state and when the state re-renders with the new values it will be passed on to the use super cluster package which will give us the new clusters um however they should appear for the user based on their zoom level and where the Maps looking at so with the clusters what this is is an array of points basically that we want to show on the map so clusters dot why don't I even before going there why don't I console dot log them out so you can see what they look like like that okay so I can open up dev tools so we can see what's in the console and what you can see here sort of at the level where zoomed out there's actually only a single cluster so if I open that up I can see properties this time I'm seeing cluster true um I've got the idea of the cluster and how many points are in it so there's 200 points because I'm still only slicing the entirety of the data but if I were to zoom in you can see that it rear Enders and now I've got 2 clusters if i zoom in again now I've got 7 clusters of data and so the first one you can see there's a hundred and 26 points let me just go and open up this one and here you can see that this one actually isn't a cluster so maybe it's a badly named but the clusters array contains a combination of actual clusters so where the cluster is true and it represents multiple points within it but it also returns individual points so if an area on the map doesn't contain ten if there's no reason to cluster them it actually gives you the individual point to display so here we can use this property again cluster false to build a differentiate between a cluster and an individual point which in our case represents a crime so now that we've console dot log data I'm just gonna remove that again and let's go down and map these out so clusters dot map so each one will just call a cluster but remember that it could either be an actual cluster or it could represent a single point a crime and I want to extract a few variables from each of these to make it easier later on so I want the longitude and the latitude and this comes from clusters sorry not clusters cluster dot geometry dot coordinates so cluster geometry dot coordinates and that's an array that it will be structure into two variables here latitude or longitude and latitude I also want to access a couple properties the first property I want is the whether it's a cluster or not so we can differentiate between the two types of data so it's a cluster property but we will call it is cluster and we also had one called point count that I will just put into a variable here called point count with this type of formatting for the variable so this is in cluster dot properties cool so with all these variables set up we can now differentiate between so we can say if it is a cluster return one thing one type of marker else or sort of below here what we actually want to show is this individual marker so we'll say return this marker and we need to fix this data so it's not crime tidy in this case it would actually be clustered up properties Crom ID and the reason this is here is because as I was mapping the crimes I added this as a property so we need to pass the latitude which is just in variable called latitude and the longitude as well and the rest should sort of work as is so that means I can remove this and we're only going to be mapping the clusters now so when I load the page for the first time you can see I'm only seeing one of them if i zoom in I see two now I see a bunch more a bunch more so what I'm seeing are right now only when it's not a cluster of data but it's an individual point so there's other clusters here we're not seeing yet and that's because we haven't filled in what to return when it is a cluster so when it is a cluster we still need a marker which has a key so cluster ID because each cluster actually I think it's I think if there's a cluster dot ID here we'll see you later we need latitude which is latitude we need longitude which is longitude and inside our marker we can put whatever we want I'm going to put a div and this div will have a class of believe I set it up to be called cluster marker and in here why don't we display the point count sort of how many points are within each cluster so if we come here as we zoom in we're viewing clusters already it doesn't look great yeah it looks like this weird egg shape that says 200 but you can see that we've got 192 now we got 2 clusters if we zoom in a little bit more we've got a few clusters plus a couple individual points and there as you zoom in they're breaking out into smaller group of points and it's exposing more individual crimes to us so why don't we fix it looking like an ugly egg and we can do that by adding on a style property and I'm going to be setting the width and the height and we're gonna set the width and the height based on the number of points that are in each cluster so we'll set up backtick so we can embed a variable so I'm going to use the point count actually we'll start it off say everyone is at least 10 pixels wide and we'll add on the point count divided by all of the points so points dot length this will basically give us what percentage is this cluster of the entire of all the points and we'll multiply that by 20 so that we can say gives us 5 percent or 0.5 I guess that'll be point zero five right so it'd be point zero five times 20 whatever that number turns out to I don't know so we set the width now we go and we set the height to be the same thing and now you can see that it's a proper circle we're setting the width and height and as we zoom in it's breaking out and the ones that have less points because we're doing a percentage of sort of all of the points here and we're multiplying that by 20 the ones that have less points will be smaller sizes and we can play with this number we can make this 40 to make it sort of more pronounced the more points that it is the bigger the circle and as we zoom in we'll get smaller and smaller points you can play with these numbers to figure out what works best for you let's stick with with 30 so up into this point we have displayed a map we fetched some remote data we've converted that remote data into the correct points format that will allow us to use supercluster and as we zoom in it sort of breaks apart into smaller clusters and individual points now what we can do is come up and there's no reason to have to slice this data anymore because our map should be able to handle the 1500 points that it has and you can see that like a zoom in zoom around like the maps really responsive I can see individual points and we've sort of fixed the problem of the map struggling to display as many markers as we were trying to show it the next thing I want to do is when I click sort of one of these points if I do a single click on it um I wanted to zoom in to the point where it breaks that marker apart into smaller clusters so we're going to do that by adding on an on-click event to the cluster or marker so one click it's a function than tickets called and what we're going to do is supercluster actually provides us some functionality to get the zoom level that we need to expand one cluster into many in order to use super cluster we can access that from what's returned from the hook here and we want to create a variable that will call expansion zoom so this is the zoom level we need for a marker for a cluster to expand into multiple because it could return us a zoom that's like too far in that we want let's say let's take the min between whatever supercluster gives us in 2020 is the most we ever want to zoom in and we access it by saying super cluster dot get cluster expansion zoom and it works by passing in the cluster ID that's what it uses to find the cluster and figure out how what Zuma needs to expand that so with that we can now interact with the map and tell the map to zoom in so this is us zooming in the map rather than the user explicitly doing it so you may have wondered why I set up this map ref if I hadn't even used it yet this is the point that we use it so we access the map referent which gives us the Google Maps itself the object and it gives us a couple methods we can call the first is set zoom so we can tell it to zoom into a certain point so let's just check if that's working so if I click in now it's zooming in to the next level say I'm here it's zooming in but see I zoomed in and now I can't even see the things I was zooming into so maybe what we wanted to do is pan to the point of the map so that whatever marker I clicked is now the center so we can do that by calling another function called set pan where we pass in the latitude and longitude that we want the map to pan to so now as I click these markers oh boy okay set pan is not a function let's see this is the finished product so I've done this and I've still messed it up which is great pan - okay take back everything I said it's not set pan it's pan - I've done this example like twice now and I always forget what functions and variables you you need to make it work so bear with me please so with pen - now let's go back and say I click down here it's going to pan to that latitude and longitude so that the marker I clicked is in the centre so every time I click it it's zooming into the point which that one cluster will break into multiple ones so this one 14 this 55 20 at some point it won't sort of break out and expand anymore because we've said it to be a max of of 20 that we want it to allow to zoom into so that's it for today just to rehash what we covered we have implemented Google Maps in react so we used this google map react package and we did some initial setup to be able to set the keys set the center in the zoom to make it show up once we had it showing up we went and we fetched our remote data we eventually took that remote data and converted it into the right format that's needed to basically cluster the data so with the clusters we could then come down and show those clusters on the map so we loop through all of each of the clusters and we differentiated between an actual clustering of points by this is cluster variable here and if it was a cluster we showed one type of marker where the size was based on how many points are contained within that cluster and if that's false we come down here and we return a marker that represents an individual crime and that was displayed as a button with some handcuffs inside of that button as the image and with that we were able to add an on-click event and using the actual super cluster instance we got the the level of zoom it needs to expand one cluster into multiple and we interacted with the actual instance of the Google map to tell it to set the zoom to this new level and also pan the map to the latitude and the longitude of the cluster and that gives us this here where we can zoom in and out and our clusters expand and break into smaller clusters as we zoom in and out that's it for the video I will link the actual source code I will link the deployed version of this and I will also link the article that I used as wellnot and making this video that will walk you through all of the steps and described in detail of what you need to do to make it work hope you enjoyed it take care everyone bye
Info
Channel: Leigh Halliday
Views: 14,977
Rating: undefined out of 5
Keywords: google maps, maps, react, javascript, clustering
Id: -NI5e_GTIko
Channel Id: undefined
Length: 33min 34sec (2014 seconds)
Published: Mon Jan 06 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.