npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

crowded-google-map

v2.0.0

Published

Display a lot of POIs on Google Map without sacrificing UX

Downloads

7

Readme

Crowded Google Map - because sometimes it gets crowded

Display a lot of POIs on Google Map without sacrificing UX

Demo

Prerequisites

Before initializing the crowded google map, make sure you have loaded Google Maps API.

Read how to do it at https://developers.google.com/maps/documentation/javascript/tutorial

Basic usage

new CrowdedGoogleMap({
  container: document.querySelector(".google-map"),
  googleMapsConfig: {
    center: { lat: -34.397, lng: 150.644 },
    zoom: 10
  },
  markersData: new Promise(resolve =>
    resolve([
      {
        first_name: "Michael",
        last_name: "Scott",
        position: { lat: 34.210973, lng: -118.436232 }
      },
      {
        first_name: "Kevin",
        last_name: "Malone",
        position: { lat: -25.363, lng: 131.044 }
      }
    ])
  )
});

Advanced Usage

const defaultClusterStyle = {
  textColor: "white",
  textSize: 12,
  height: 50,
  width: 50
};

new CrowdedGoogleMap({
  container: document.querySelector(".google-map"),
  googleMapsConfig: {
    center: { lat: -34.397, lng: 150.644 },
    zoom: 10,
    mapTypeId: "satellite"
  },
  markersData: fetch("/api/getmarkers.json", {
    credentials: "same-origin"
  }).then(res => resolve(res.json())),
  parseMarkerData: marker => {
    return Object.assign({}, marker, {
      name: `${marker.first_name} ${marker.last_name}`,
      href: `http://google.com/?q=${marker.first_name}+${marker.last_name}`,
      icon: {
        url: `http://my.cdn.com/images/gmap/${marker.type}-icon.svg`,
        size: new google.maps.Size(24, 24),
        origin: new google.maps.Point(0, 0),
        anchor: new google.maps.Point(0, 24)
      }
    });
  },
  infoWindowConfig: marker => {
    return {
      content: `<a href="${marker.href}">${marker.name}</a>`,
      maxWidth: 640
    };
  },
  clustererConfig: {
    styles: [
      Object.assign({}, defaultClusterStyle, {
        url: `http://my.cdn.com/images/gmap/small.png`
      }),
      Object.assign({}, defaultClusterStyle, {
        url: `http://my.cdn.com/images/gmap/big.png`
      })
    ]
  }
});

Options

container

Type: DOM Element

Example:

document.querySelector(".google-map");

googleMapsConfig

Type: Object

Example:

{
  zoom: 6,
  center: { lat: -34.397, lng: 150.644 },
  mapTypeId: "satellite"
}

Config that will be passed to Google Maps constructor. You can use everything that Google Maps supports (ie. styling).

Read more about google maps api options at https://developers.google.com/maps/documentation/javascript/3.exp/reference

clustererConfig

Type: Object

Example:

{
  gridSize: 30,
  maxZoom: 12
}

Object that will extend/override default clusterer config that can be found in src/config.

Read more about supported options at https://www.npmjs.com/package/node-js-marker-clusterer

spiderifyConfig

Type: Object

Example:

{
  circleFootSeparation: 30,
  nearbyDistance: 60
}

Object that will extend/override default spiderify config that can be found in src/config.

Read more about supported options at https://www.npmjs.com/package/overlapping-marker-spiderfier

markersData

Type: Promise (resolving to an array of objects)

Example:

fetch("/api/getmarkers.json", {
  credentials: "same-origin"
}).then(res => resolve(res.json()));

Collection of objects that will be used on the map. They will be parsed later on if you pass parseMarkerData.

parseMarkerData

Type: Function

Example:

const parseMarkerData = marker => {
  return Object.assign({}, marker, {
    // Take whatever has been passed as marker, extend using object passed below
    name: `${marker.first_name} ${marker.last_name}`,
    href: `https://google.com/?q=${marker.first_name}+${marker.last_name}`,
    icon: {
      url: `https://my.cdn.com/images/gmap/${marker.type}-icon.svg`,
      size: new google.maps.Size(24, 24),
      origin: new google.maps.Point(0, 0),
      anchor: new google.maps.Point(0, 24)
    }
  });
};

Function that will be run on each marker (using .map) object before turning it into google map marker. Make sure this function returns proper objects/structure/data to use in later life cycles (ie. creation of google markers, info windows)

infoWindowConfig

Type: Function

Example:

const infoWindowConfig = marker => {
  return {
    content: `<a href="${marker.href}">${marker.name}</a>`,
    maxWidth: 640
  };
};

Configuration of infoWindow based on currently created marker. Content key is required by Google.

Read more about infoWindows

Dependencies

This script includes two depencenies to handle crowded maps:

Performance tips

If your markers are served by the API (which often is the case), start downloading them as soon as possible. For example, you might want to use XHR to start downloading them in the <head>. The benefits depend on weight/speed of loading other blocking assets, so your milage may vary. In my tests I have observed 2-2.5 seconds boost by starting request as a first request on the site.

window.mapMarkers = new Promise(function(resolve) {
  return fetch("/getMarkers.json", { credentials: "same-origin" }).then(
    function(res) {
      return resolve(res.json());
    }
  );
});

Having this set up you can pass window.mapMarkers as markersData to the map constructor - it will initialize as much as possible without the markers, then continue when they arrive.

Testing

Run npm test to open test page with (hopefully) working example.

Troubleshooting

My map isn't showing up at all

Remember to provide some kind of css to make sure the map has height. You can even do that inline.

<div data-crowded-google-map="container" style="height: 700px"></div>

My clusters are missing icons

Make sure you pass styles with proper urls to graphic icons. See Advanced Usage example.

Known bugs

Uncaught TypeError: Cannot read property 'fromLatLngToDivPixel' of undefined in oms.js. Everything works, if you have any idea why it throws error anyway please help me fix it :)