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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@myrmidon/cadmus-geo-location

v1.0.0

Published

Cadmus - geographical location component.

Readme

CadmusGeoLocation

Cadmus geographical location editor brick, built with Angular and MapLibre GL JS via ngx-maplibre-gl.

Setup

1. Install Peer Dependencies

The library requires MapLibre GL and its Angular wrapper, plus a WKT parser and GeoJSON types:

npm install @maplibre/ngx-maplibre-gl maplibre-gl @terraformer/wkt @types/geojson

The library also uses @myrmidon/ngx-mat-tools for its DialogService (confirmation dialogs). If you don't already have it:

npm install @myrmidon/ngx-mat-tools

2. Import MapLibre CSS

The MapLibre GL stylesheet must be imported globally. Without it, the map will render with broken layout and missing controls.

Option A (suggested) -- in your global styles.scss (or styles.css):

@import "maplibre-gl/dist/maplibre-gl.css";

Option B -- in angular.json, under projects > your-app > architect > build > options > styles:

"styles": [
  "node_modules/maplibre-gl/dist/maplibre-gl.css",
  "src/styles.scss"
]

3. CommonJS Warning (Optional)

MapLibre GL is distributed as CommonJS. Angular CLI will emit a warning during build:

Module 'maplibre-gl' is not ESM

This is harmless. To silence it, add to angular.json under build > options:

"allowedCommonJsDependencies": ["maplibre-gl"]

Model

A GeoLocation represents a simple geographic location:

interface GeoLocation {
  eid?: string; // optional entity ID
  label: string; // display label (required)
  latitude: number; // decimal degrees, -90 to 90 (required)
  longitude: number; // decimal degrees, -180 to 180 (required)
  altitude?: number; // meters
  radius?: number; // uncertainty radius in meters
  geometry?: string; // area geometry (WKT or GeoJSON string)
  note?: string; // free text note
}

Example:

{
  "eid": "place-123",
  "label": "Ancient Settlement X",
  "latitude": 41.8902,
  "longitude": 12.4922,
  "altitude": 35.0,
  "geometry": "POLYGON((12.48 41.89, 12.49 41.89, 12.49 41.90, 12.48 41.90, 12.48 41.89))"
}

GeoLocationEditor

Interactive editor for a single GeoLocation. It shows a form for all properties side by side with a MapLibre map. The map displays the marker, geometry overlay, radius circle, and label. Users can draw geometries (point, circle, rectangle, polygon) directly on the map.

Component Reference

  • ▶️ input:
  • 🔑 class: GeoLocationEditor
  • 🚩 selector: cadmus-geo-location-editor
  • ▶️ inputs:
    • location (GeoLocation?): the location to edit (two-way bindable via model).
    • geometryFormat (GeoLocationGeometryFormat): format for the geometry field. Default is WKT (Well-Known Text), because it is an OGC standard, simple, compact and human-readable, and also fits in data mostly serialized into JSON because it's an implementation-neutral string. Alternatively you can use GeoJSON. Alternatively use GeoJSON.
    • noLocateButton (boolean): set to true to hide the browser geolocation button.
    • mapStyle (string): map tile style URL. Defaults to https://basemaps.cartocdn.com/gl/voyager-gl-style/style.json.
  • 🔥 outputs:
    • locationChange: emitted when the user saves the location.
    • cancelEdit: emitted when the user cancels.

Usage Example

import { Component } from "@angular/core";
import { GeoLocation, GeoLocationEditor } from "@myrmidon/cadmus-geo-location";

@Component({
  selector: "app-my-page",
  imports: [GeoLocationEditor],
  template: ` <cadmus-geo-location-editor [location]="location" (locationChange)="onLocationChange($event)" (cancelEdit)="onCancel()" /> `,
})
export class MyPageComponent {
  public location: GeoLocation = {
    label: "Rome",
    latitude: 41.9028,
    longitude: 12.4964,
  };

  public onLocationChange(location: GeoLocation): void {
    this.location = location;
  }

  public onCancel(): void {
    // handle cancel
  }
}

Map Toolbar

The map toolbar (above the map) provides:

| Button | Icon | Description | | -------------- | --------------------- | --------------------------------------------------------------------------------------------- | | Recenter | center_focus_strong | Re-centers the map on the current point. | | Centroid | filter_center_focus | Sets lat/lng from the centroid of the current geometry (enabled only when a geometry exists). | | Draw toggle | edit / edit_off | Enters or exits drawing mode. When exiting, any drawn geometry is committed to the form. | | Point tool | place | (drawing mode) Click the map to place/move the point. | | Circle tool | circle | (drawing mode) Click center, then click to set radius. | | Rectangle tool | crop_square | (drawing mode) Click two opposite corners. | | Polygon tool | pentagon | (drawing mode) Click vertices, double-click to finish. | | Clear | delete_outline | (drawing mode) Clears the point, geometry, radius, and all overlays so you can start fresh. |

Browser Geolocation

The locate button (my_location) uses the browser Geolocation API with high-accuracy mode. After a successful fix, an accuracy indicator icon appears next to the button with a color-coded tooltip:

| Accuracy | Icon | Color | Likely Source | | ---------- | --------------------------- | ----------- | ------------- | | < 20 m | gps_fixed | green | GPS | | 20-100 m | signal_cellular_alt_1_bar | light green | Wi-Fi | | 100-1000 m | signal_cellular_alt_2_bar | orange | Cell tower | | > 1000 m | signal_cellular_alt | red | IP-based |

Two-Way Map Sync

The form and map stay in sync in both directions:

  • Form to map: editing latitude, longitude, or geometry in the form updates the map (marker position, geometry overlay, radius circle, label) in real-time after a 400 ms debounce.
  • Map to form: drawing on the map or dragging the marker updates the form controls. Drawn geometries are committed to the form when exiting drawing mode.

Exported Utilities

The library also exports standalone helper functions and services:

WktService

Injectable service for converting between geometry strings and GeoJSON:

  • toGeoJSON(geometry, format): parses a WKT or GeoJSON string into a GeoJSON.Geometry object.
  • fromGeoJSON(geojson, format): serializes a GeoJSON.Geometry to WKT or GeoJSON string.

geo-helper Functions

Pure functions for geometric calculations:

  • createCirclePolygon(center, radiusMeters, steps?): generates a GeoJSON Polygon approximating a circle.
  • createRectanglePolygon(corner1, corner2): generates a GeoJSON Polygon for a rectangle.
  • haversineDistance(p1, p2): calculates the distance in meters between two [lng, lat] points.
  • computeCentroid(geometry): computes the [lng, lat] centroid of any GeoJSON Geometry.

History

0.0.5

  • 2026-02-22: refactor map update trigger on form change to avoid "bump" effect when typing in form controls.

0.0.4

  • 2026-02-15: fix to locate user in zone-enabled apps: here's what happened in zone-enabled workspaces: when clicking the locate button which uses the browser geolocation service to set a new position in the map, the position is set correctly, with its pin and note. Yet, while in the original workspace the map "flies" to the new location without issues, up to the expected zoom level, in a zone-enabled app it starts zooming but it then stops abruptly. Here is what probably happened:
  1. locateUser() sets form values (latitude.setValue, longitude.setValue, etc.) and then calls mapCenter.set() + mapZoom.set([14])
  2. The mgl-map component detects center/zoom changes via ngOnChanges → calls flyTo → animation starts
  3. 400ms later, the form.valueChanges debounce fires (line 270) → calls syncMapCenter() which sets mapCenter to the same coordinates but a new array reference
  4. Angular sees this as a changed input → ngOnChanges fires again → flyTo is called again, interrupting the in-progress animation

In a zoneless app, the debounced setTimeout doesn't trigger change detection, so the second flyTo never happens. With Zone.js, setTimeout (used internally by debounceTime) triggers CD, which propagates the signal change. The fix: guard locateUser() with _updatingForm = true (same pattern used by updateForm) to prevent the form.valueChanges subscriber from firing redundantly.

0.0.3

  • 2026-02-15: fix to race condition. In @maplibre/ngx-maplibre-gl v21, both MarkerComponent and PopupComponent use afterNextRender() to initialize. When both are created in the same render cycle and the popup tries to attach to the marker via [marker], it reads marker.markerInstance() which can still be null if the marker's afterNextRender hasn't set it yet. This timing is environment-dependent, which explains why it works here, but fails in other workspaces (where the component nesting is deeper).

0.0.2

  • 2026-02-12: changed DEFAULT_MAP_STYLE from CARTO Voyager to OpenFreeMap Liberty. OpenFreeMap returns 200 OK with CORS headers even for empty tiles (instead of CARTO's 404 without CORS headers), and requires no API key. In template, added 'text-font': ['Noto Sans Regular'] to the label-text layer layout. Without this, MapLibre defaults to "Open Sans Regular,Arial Unicode MS Regular" which only CARTO's font server has. Noto Sans Regular is what the OpenFreeMap Liberty style uses and serves.

0.0.1

  • 2026-02-11: initial release.