PHPackages                             yasseralsamman/saudi-national-address - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. yasseralsamman/saudi-national-address

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

yasseralsamman/saudi-national-address
=====================================

Open dataset of Saudi Arabia regions, cities, and districts, with SPL-compatible 11-digit district IDs.

v1.1.0(1mo ago)016↓66.7%MITTypeScriptPHP ^8.1CI passing

Since Jun 1Pushed 1mo agoCompare

[ Source](https://github.com/yasseralsamman/saudi-national-address)[ Packagist](https://packagist.org/packages/yasseralsamman/saudi-national-address)[ RSS](/packages/yasseralsamman-saudi-national-address/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (2)Dependencies (1)Versions (3)Used By (0)

Saudi National Address Open Dataset
===================================

[](#saudi-national-address-open-dataset)

An open, developer-friendly dataset of Saudi Arabia's **13 regions, 4,581 cities, and 3,732 districts**, with the official SPL National Address **11-digit composite `district_id`**, district boundary polygons, and a dozen ready-to-use output formats. It is a drop-in replacement for the `homaily/Saudi-Arabia-Regions-Cities-and-Districts`dataset, packaged for both JavaScript/TypeScript (npm) and PHP (Composer), with a static map viewer and automated data-integrity checks.

What's in this repo
-------------------

[](#whats-in-this-repo)

- **Data** — one canonical source (`data/source/`) compiled into many formats under `data/dist/`: full + lite JSON, GeoJSON, TopoJSON, CSV, and MySQL/PostgreSQL SQL.
- **Packages** — `saudi-national-address` on npm (TypeScript, ESM + CJS) and `yasseralsamman/saudi-national-address` on Packagist (PHP 8.1+), both bundling the data so consumers never fetch over HTTP at runtime.
- **Viewer** — a single-page Leaflet map (no build step) for browsing regions and districts, deployable to GitHub Pages.

Quick links
-----------

[](#quick-links)

- npm:
- Packagist:
- Map viewer:
- Data dictionary: [`DATA_REFERENCE.md`](DATA_REFERENCE.md)

Data formats available
----------------------

[](#data-formats-available)

All generated files live in [`data/dist/`](data/dist/) and are committed for direct download. The bare-array JSON files (`*.full.json` / `*.lite.json`) keep the exact shape of the upstream homaily dataset.

FilePurpose[`regions.full.json`](data/dist/regions.full.json)13 regions with `population`, `center`, `bbox`, and boundary polygons.[`regions.lite.json`](data/dist/regions.lite.json)13 regions without geometry — ids, names, `center`, `bbox`.[`cities.full.json`](data/dist/cities.full.json)4,581 cities (ids, names, `center`).[`cities.lite.json`](data/dist/cities.lite.json)Identical to the full file (cities have no boundaries).[`districts.full.json`](data/dist/districts.full.json)3,732 districts with `local_seq`, `center`, `bbox`, and boundary polygons.[`districts.lite.json`](data/dist/districts.lite.json)3,732 districts without polygons — the file most consumers want.[`regions.geojson`](data/dist/regions.geojson)Region polygons in standard GeoJSON (`[lon, lat]`).[`districts.geojson`](data/dist/districts.geojson)District polygons in standard GeoJSON (`[lon, lat]`).[`regions.topojson`](data/dist/regions.topojson)TopoJSON of the regions (quantized, ~80% smaller).[`districts.topojson`](data/dist/districts.topojson)TopoJSON of the districts (quantized).[`regions.csv`](data/dist/regions.csv)Flat region rows (`center`/`bbox` split into columns).[`cities.csv`](data/dist/cities.csv)Flat city rows.[`districts.csv`](data/dist/districts.csv)Flat district rows.[`mysql.sql`](data/dist/mysql.sql)Self-contained MySQL dump (`utf8mb4`, three tables).[`postgres.sql`](data/dist/postgres.sql)Self-contained PostgreSQL dump.A SQLite build (`saudi-national-address.sqlite`) is attached to each GitHub Release rather than committed to the repository.

JSON Schemas for the three record types are in [`schemas/`](schemas/).

Install
-------

[](#install)

### JavaScript / TypeScript

[](#javascript--typescript)

```
npm install saudi-national-address
```

Lite by default (no geometry — small bundle):

```
import { districts, findDistrict, decodeDistrictId } from 'saudi-national-address';

console.log(districts.length); // 3732
findDistrict(10100003001);     // { district_id: 10100003001, city_id: 3, ... }  (no boundaries)
decodeDistrictId(10100003001); // { prefix: 1, region_id: 1, city_id: 3, local_seq: 1 }
```

Full data (boundary polygons + region `population`) from the `/full` subpath — import it only when you need geometry, so lite-only apps never bundle it:

```
import { districtsFull, regionsFull, findDistrictFull } from 'saudi-national-address/full';

regionsFull[0].population;            // number
findDistrictFull(10100003001)?.boundaries; // polygon rings ([lat, lon])
```

See [`packages/js/README.md`](packages/js/README.md) for the full API.

### PHP

[](#php)

```
composer require yasseralsamman/saudi-national-address
```

Lite by default:

```
use SaudiNationalAddress\Dataset;
use SaudiNationalAddress\DistrictId;

count(Dataset::districts());            // 3732
Dataset::findDistrict(10100003001);     // District { city_id: 3, ... }  (no boundaries)
DistrictId::decode(10100003001);        // ['prefix' => 1, 'region_id' => 1, 'city_id' => 3, 'local_seq' => 1]
```

Full data (boundary polygons + region `population`), loaded on demand:

```
Dataset::findRegionFull(1)->population;           // int
Dataset::findDistrictFull(10100003001)->boundaries; // polygon rings ([lat, lon])
count(Dataset::districtsFull());                  // 3732, each with boundaries
```

See [`packages/php/README.md`](packages/php/README.md) for the full API.

The 11-digit `district_id` format
---------------------------------

[](#the-11-digit-district_id-format)

`district_id` is **not** a flat sequential identifier. It is an **11-digit composite** that encodes the region, the city, and a per-city district sequence number, matching the form used by SPL's National Address API (which documents the example value `10100003001`).

### Layout

[](#layout)

```
Position:  1   2 3   4 5 6 7 8   9 10 11
Digit:     1   0 1   0 0 0 0 3   0  0  1
Group:    [P] [ R R ][ C C C C C ][ N N N ]

```

GroupDigitsMeaningP1Constant prefix, always `1` (KSA country marker in the SPL scheme)RR2`region_id`, zero-padded to 2 digitsCCCCC5`city_id`, zero-padded to 5 digitsNNN3District sequence number within the city (1, 2, 3, …), zero-padded to 3 digitsTotal: 11 digits. Fits comfortably in a 64-bit integer, in JavaScript's safe integer range (`Number.MAX_SAFE_INTEGER` ≈ 9 × 10¹⁵), and in a PHP `int` on 64-bit systems.

### Verified samples (drawn directly from the dataset)

[](#verified-samples-drawn-directly-from-the-dataset)

district\_idPRRCCCCCNNNmatches `region_id` / `city_id`?`10100003001`10100003001region 1, city 3 ✓`10101351033`10101351033region 1, city 1351 ✓`10103158001`10103158001region 1, city 3158 ✓`10400011038`10400011038region 4, city 11 ✓`10502167006`10502167006region 5, city 2167 ✓The embedded `RR` and `CCCCC` always match the row's `region_id` and `city_id`. This is enforced by the build's validation step.

### Decoding (language-agnostic)

[](#decoding-language-agnostic)

```
prefix         = floor(district_id / 10_000_000_000)        // always 1
region_part    = floor(district_id / 100_000_000) % 100     // 2-digit region
city_part      = floor(district_id / 1_000)       % 100_000 // 5-digit city
local_seq      = district_id % 1_000                        // 3-digit sequence

```

### JavaScript / TypeScript

[](#javascript--typescript-1)

```
import { decodeDistrictId, encodeDistrictId } from 'saudi-national-address';

decodeDistrictId(10100003001);
// → { prefix: 1, region_id: 1, city_id: 3, local_seq: 1 }

encodeDistrictId(1, 3, 1);
// → 10100003001
```

### PHP

[](#php-1)

```
use SaudiNationalAddress\DistrictId;

DistrictId::decode(10100003001);
// → ['prefix' => 1, 'region_id' => 1, 'city_id' => 3, 'local_seq' => 1]

DistrictId::encode(1, 3, 1);
// → 10100003001
```

### Why keep the long form?

[](#why-keep-the-long-form)

The 11-digit composite is the canonical SPL-API form and round-trips losslessly to the shorter per-city sequence (`local_seq = district_id % 1000`). Storing only the short form would lose the parent IDs and require a separate join to reconstruct them. Every record in this dataset also carries `region_id` and `city_id` as explicit fields, so consumers can use whichever representation they prefer.

For the complete data dictionary, see [`DATA_REFERENCE.md`](DATA_REFERENCE.md).

Coverage
--------

[](#coverage)

- **13** administrative regions
- **4,581** cities
- **3,732** districts

What's not included
-------------------

[](#whats-not-included)

- **Postal codes** — the source data does not contain per-district postal codes, and none are invented here.
- **Governorates (محافظة)** — the source data has no governorate layer.
- **A hosted lookup API** — use SPL's `api.address.gov.sa` for live address lookups.
- **A Drupal module** — not part of this dataset repository.

Development
-----------

[](#development)

```
pnpm install
pnpm run validate   # data-integrity checks against data/source/
pnpm run build      # regenerate everything in data/dist/ and the package data/ dirs
pnpm test           # run the JS package tests
```

The PHP package is tested with:

```
composer install --working-dir=packages/php
./packages/php/vendor/bin/phpunit -c packages/php/phpunit.xml.dist
```

`data/dist/` is committed and CI enforces that it matches a fresh build (`pnpm run check:dist-clean`). To correct data, edit `data/source/` and re-run `pnpm run build` — never hand-edit `data/dist/`.

License
-------

[](#license)

- **Code** (scripts, both packages, the viewer): [MIT](LICENSE).
- **Data** (`data/source/` and `data/dist/`): [CC0-1.0](LICENSE-DATA) — a public-domain dedication, so no attribution is required and there is no share-alike obligation. Attribution is appreciated as a courtesy but not legally required.

Acknowledgements
----------------

[](#acknowledgements)

- The underlying region/city/district boundary data comes from the [`homaily/Saudi-Arabia-Regions-Cities-and-Districts`](https://github.com/homaily/Saudi-Arabia-Regions-Cities-and-Districts)dataset.
- The National Address standard, region codes, and the 11-digit `district_id` form are defined by **SPL — Saudi Post Logistics** (), the upstream authority.

Publishing &amp; identifiers
----------------------------

[](#publishing--identifiers)

This tree ships pre-configured for:

- GitHub owner: **yasseralsamman** (`https://github.com/yasseralsamman/saudi-national-address`)
- npm package: **saudi-national-address**
- Packagist vendor: **yasseralsamman/saudi-national-address**

To publish: push `main`, then `git tag v1.0.0 && git push --tags`. The release workflow publishes to npm and creates the GitHub Release (with the SQLite + asset bundle), and Packagist auto-syncs the PHP package. Enable GitHub Pages after the first `pages.yml` run.

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance90

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

52d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/220514259?v=4)[yassersamman](/maintainers/yassersamman)[@yassersamman](https://github.com/yassersamman)

---

Top Contributors

[![yasseralsamman](https://avatars.githubusercontent.com/u/10357655?v=4)](https://github.com/yasseralsamman "yasseralsamman (6 commits)")

---

Tags

addresssplcitiesregionssaudi-arabiadistrictssaudinational-address

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/yasseralsamman-saudi-national-address/health.svg)

```
[![Health](https://phpackages.com/badges/yasseralsamman-saudi-national-address/health.svg)](https://phpackages.com/packages/yasseralsamman-saudi-national-address)
```

###  Alternatives

[pragmarx/countries

PHP Countries and Currencies

1.9k3.4M24](/packages/pragmarx-countries)[mlocati/ip-lib

Handle IPv4, IPv6 addresses and ranges

3167.3M73](/packages/mlocati-ip-lib)[markrogoyski/ipv4-subnet-calculator

Network calculator for subnet mask and other classless (CIDR) network information.

175852.6k14](/packages/markrogoyski-ipv4-subnet-calculator)[pragmarx/countries-laravel

Countries for Laravel

1491.1M2](/packages/pragmarx-countries-laravel)[gerardojbaez/geodata

Laravel 5.2 package that provides basic geographical data like Countries, Regions and Cities.

3112.0k](/packages/gerardojbaez-geodata)[tapp/filament-google-autocomplete-field

Filament plugin that provides a Google Autocomplete field

30136.4k](/packages/tapp-filament-google-autocomplete-field)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
