Generate and display maps without external services or compromising on privacy.

Overview

Banner

PHP Geo SVG

GitHub Build Status Scrutinizer Code Quality Code Coverage

Generate and display maps without external services or compromising on privacy.

Why this package?

When searching for high quality SVG's for my blog, I ran into the issue that all SVG's where either too low quality, heavily polluted with unnecessary elements by the tool they were generated with, or in the wrong projection. This package provides the flexibility to generate maps manually or from geojson files, which are more readily available.

Features

  • Supports creation of SVGs from GeoJSON (directly from file, as a JSON string or from an array) and manual GeometryCollection.
  • Output the svg directly to your user on request or output to a file.
  • Render an entire world map or only part of it by using a bounding box.
  • Easy support for projections. Currently supported: EquiRectangular, Mercator and Miller. Please open a PR to add any extra ones.
  • When you create or edit a GeometryCollection, a Fluent Interface is provided to allow for method chaining.
Showcase
Countries - Equirectangular projection
Netherlands - Mercator projection

Table of Contents

Setup

To start right away, run the following command in your composer project;

composer require prinsfrank/php-geo-svg

Or for development only;

composer require prinsfrank/php-geo-svg --dev

The basics; creating an SVG

Let's say we want to create the following simple continent map:

Simplified continents map

There are multiple ways we can go about creating this map in svg using this package;

From a GeoJSON file

To create an SVG from a GeoJson file, create a new 'GeometryCollection' by calling the 'createFromGeoJSONFilePath' method on the 'GeometryCollectionFactory' as follows;

Show code

With variables:

$geoSVG = new GeoSVG();
$geometryCollection = GeometryCollectionFactory::createFromGeoJSONFilePath('/path/to/file.geojson');
$geoSVG->toFile($geometryCollection, 'output/file.svg');

Fluent:

(new GeoSVG())
    ->toFile(
        GeometryCollectionFactory::createFromGeoJSONFilePath(__DIR__ . '/path/to/file.geojson'),
        'output/file.svg'
    );

From a GeoJSON string

To create an SVG from a GeoJson string, create a new 'GeometryCollection' by calling the 'createFromGeoJsonString' method on the 'GeometryCollectionFactory' as follows;

Show code

With variables:

toFile($geometryCollection, 'output/file.svg'); ">
$geoJsonString = '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-177,74],[-80,9],[-25,82]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-80,9],[-37,-7],[-70,-55]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-12,36],[30,37],[27,70],[-24,66]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-12,36],[30,37],[51,11],[22,-35],[-17,17]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[27,70],[30,37],[51,11],[131,-2],[171,67]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[115,-15],[153,-15],[148,-43],[114,-35]]]}}]}';


$geoSVG = new GeoSVG();
$geometryCollection = GeometryCollectionFactory::createFromGeoJsonString($geoJsonString);
$geoSVG->toFile($geometryCollection, 'output/file.svg');

Fluent:

toFile( GeometryCollectionFactory::createFromGeoJsonString($geoJsonString), 'output/file.svg' ); ">
$geoJsonString = '{"type":"FeatureCollection","features":[{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-177,74],[-80,9],[-25,82]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-80,9],[-37,-7],[-70,-55]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-12,36],[30,37],[27,70],[-24,66]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[-12,36],[30,37],[51,11],[22,-35],[-17,17]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[27,70],[30,37],[51,11],[131,-2],[171,67]]]}},{"type":"Feature","properties":{"featurecla":"Continent"},"geometry":{"type":"MultiLineString","coordinates":[[[115,-15],[153,-15],[148,-43],[114,-35]]]}}]}';

(new GeoSVG())
    ->toFile(
        GeometryCollectionFactory::createFromGeoJsonString($geoJsonString),
        'output/file.svg'
    );

From a GeoJSON array

To create an SVG from a GeoJson array, create a new 'GeometryCollection' by calling the 'createFromGeoJSONArray' method on the 'GeometryCollectionFactory' as follows;

Show code

With variables:

$geoJsonArray = ['type'=>'FeatureCollection','features'=>[['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-177,74],[-80,9],[-25,82]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-80,9],[-37,-7],[-70,-55]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-12,36],[30,37],[27,70],[-24,66]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-12,36],[30,37],[51,11],[22,-35],[-17,17]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[27,70],[30,37],[51,11],[131,-2],[171,67]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[115,-15],[153,-15],[148,-43],[114,-35]]]]]]];


$geoSVG = new GeoSVG();
$geometryCollection = GeometryCollectionFactory::createFromGeoJsonArray($geoJsonArray);
$geoSVG->toFile($geometryCollection, 'output/file.svg');

Fluent:

$geoJsonArray = ['type'=>'FeatureCollection','features'=>[['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-177,74],[-80,9],[-25,82]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-80,9],[-37,-7],[-70,-55]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-12,36],[30,37],[27,70],[-24,66]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[-12,36],[30,37],[51,11],[22,-35],[-17,17]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[27,70],[30,37],[51,11],[131,-2],[171,67]]]]],['type'=>'Feature','properties'=>['featurecla'=>'Continent'],'geometry'=>['type'=>'MultiLineString','coordinates'=>[[[115,-15],[153,-15],[148,-43],[114,-35]]]]]]];

(new GeoSVG())
    ->toFile(
        GeometryCollectionFactory::createFromGeoJsonArray($geoJsonArray),
        'output/file.svg'
    );

From building your own GeometryCollection

To build an SVG manually from a GeometryCollection, create the object and add any geometry you want:

Show code

With variables:

$geoSVG = new GeoSVG();
$geometryCollection = new GeometryCollection();

$continents = new MultiPolygon();
$geometryCollection->addGeometryObject($continents);

$outerBorderNorthAmerica = new LineString();
$outerBorderNorthAmerica->addPosition(new Position(-177, 74));
$outerBorderNorthAmerica->addPosition(new Position(-80, 9));
$outerBorderNorthAmerica->addPosition(new Position(-25, 82));
$outerBorderNorthAmerica->setFeatureClass('Continent');
$outerBorderNorthAmerica->setTitle('North America');
$polygonNorthAmerica = new Polygon($outerBorderNorthAmerica);
$continents->addPolygon($polygonNorthAmerica);


$outerBorderSouthAmerica = new LineString();
$outerBorderSouthAmerica->addPosition(new Position(-80, 9));
$outerBorderSouthAmerica->addPosition(new Position(-37, -7));
$outerBorderSouthAmerica->addPosition(new Position(-70, -55));
$outerBorderSouthAmerica->setFeatureClass('Continent');
$outerBorderSouthAmerica->setTitle('South America');
$polygonSouthAmerica = new Polygon($outerBorderSouthAmerica);
$continents->addPolygon($polygonSouthAmerica);

$outerBorderEurope = new LineString();
$outerBorderEurope->addPosition(new Position(-12, 36));
$outerBorderEurope->addPosition(new Position(30, 37));
$outerBorderEurope->addPosition(new Position(27, 70));
$outerBorderEurope->addPosition(new Position(-24, 66));
$outerBorderEurope->setFeatureClass('Continent');
$outerBorderEurope->setTitle('Europe');
$polygonEurope = new Polygon($outerBorderEurope);
$continents->addPolygon($polygonEurope);

$outerBorderAfrica = new LineString();
$outerBorderAfrica->addPosition(new Position(-12, 36));
$outerBorderAfrica->addPosition(new Position(30, 37));
$outerBorderAfrica->addPosition(new Position(51, 11));
$outerBorderAfrica->addPosition(new Position(22, -35));
$outerBorderAfrica->addPosition(new Position(-17, 17));
$outerBorderAfrica->setFeatureClass('Continent');
$outerBorderAfrica->setTitle('Africa');
$polygonAfrica = new Polygon($outerBorderAfrica);
$continents->addPolygon($polygonAfrica);

$outerBorderAsia = new LineString();
$outerBorderAsia->addPosition(new Position(27, 70));
$outerBorderAsia->addPosition(new Position(30, 37));
$outerBorderAsia->addPosition(new Position(51, 11));
$outerBorderAsia->addPosition(new Position(131, -2));
$outerBorderAsia->addPosition(new Position(171, 67));
$outerBorderAsia->setFeatureClass('Continent');
$outerBorderAsia->setTitle('Asia');
$polygonAsia = new Polygon($outerBorderAsia);
$continents->addPolygon($polygonAsia);

$outerBorderAustralia = new LineString();
$outerBorderAustralia->addPosition(new Position(115, -15));
$outerBorderAustralia->addPosition(new Position(153, -15));
$outerBorderAustralia->addPosition(new Position(148, -43));
$outerBorderAustralia->addPosition(new Position(114, -35));
$outerBorderAustralia->setFeatureClass('Continent');
$outerBorderAustralia->setTitle('Australia');
$polygonAustralia = new Polygon($outerBorderAustralia);
$continents->addPolygon($polygonAustralia);

$geoSVG->toFile($geometryCollection, 'output/file.svg');

Fluent:

(new GeoSVG())
    ->toFile(
        (new GeometryCollection())
            ->addGeometryObject(
                (new MultiPolygon())
                    ->addPolygon(
                        new Polygon(
                            (new LineString())
                                ->addPosition(new Position(-177, 74))
                                ->addPosition(new Position(-80, 9))
                                ->addPosition(new Position(-25, 82))
                                ->setFeatureClass('Continent')
                                ->setTitle('North America')
                        )
                    )
                    ->addPolygon(
                        new Polygon(
                            (new LineString())
                                ->addPosition(new Position(-80, 9))
                                ->addPosition(new Position(-37, -7))
                                ->addPosition(new Position(-70, -55))
                                ->setFeatureClass('Continent')
                                ->setTitle('South America')
                        )
                    )
                    ->addPolygon(
                        new Polygon(
                            (new LineString())
                                ->addPosition(new Position(-12, 36))
                                ->addPosition(new Position(30, 37))
                                ->addPosition(new Position(27, 70))
                                ->addPosition(new Position(-24, 66))
                                ->setFeatureClass('Continent')
                                ->setTitle('Europe')
                        )
                    )
                    ->addPolygon(
                        new Polygon(
                            (new LineString())
                                ->addPosition(new Position(-12, 36))
                                ->addPosition(new Position(30, 37))
                                ->addPosition(new Position(51, 11))
                                ->addPosition(new Position(22, -35))
                                ->addPosition(new Position(-17, 17))
                                ->setFeatureClass('Continent')
                                ->setTitle('Africa')
                        )
                    )
                    ->addPolygon(
                        new Polygon(
                            (new LineString())
                                ->addPosition(new Position(27, 70))
                                ->addPosition(new Position(30, 37))
                                ->addPosition(new Position(51, 11))
                                ->addPosition(new Position(131, -2))
                                ->addPosition(new Position(171, 67))
                                ->setFeatureClass('Continent')
                                ->setTitle('Asia')
                        )
                    )
                    ->addPolygon(
                        new Polygon(
                            (new LineString())
                                ->addPosition(new Position(115, -15))
                                ->addPosition(new Position(153, -15))
                                ->addPosition(new Position(148, -43))
                                ->addPosition(new Position(114, -35))
                                ->setFeatureClass('Continent')
                                ->setTitle('Australia')
                        )
                    )
            ),
        'output/file.svg'
    );

Different globe-to-plane transformations; Projections

If you prefer a different projection other than the default EquiRectangular, you can! Currently, the following projections are supported, but please feel free to add any and open a PR;

  • Equirectangular
  • Mercator
  • Miller

To specify the projection you want to use, you can do so either using the constructor;

$geoSVG = new GeoSVG(new MercatorProjection);

Or using the 'setProjection' method;

$geoSVG = new GeoSVG();
$geoSVG->setProjection(new MercatorProjection);

Displaying only parts of the world; Using bounding boxes

If you want to use a bounding box, you have to know both the most southWestern and northEastern coordinates you want to show, and create 'BoundingBoxPositions' for both of them. Pass those along to a bounding box object and you have yourself a bounding box;

$northEastern = new BoundingBoxPosition(7.2, 53.5);
$southWestern = new BoundingBoxPosition(3.5, 50.8);

$boundingBox  = new BoundingBox($southWestern, $northEastern);

To actually use it, either pass the bounding box in the constructor after the projection;

$geoSVG = new GeoSVG($projection, $boundingBox);

Or set the bounding box using the 'setBoundingBox' method;

$geoSVG = new GeoSVG();
$geoSVG->setBoundingBox($boundingBox);
You might also like...
Pretty Good Privacy (PGP) is an encryption program that provides cryptographic privacy and authentication for data communication.

Pretty Good Privacy (PGP) is an encryption program that provides cryptographic privacy and authentication for data communication. PGP is used for signing, encrypting, and decrypting texts, e-mails, files, directories, and whole disk partitions and to increase the security of e-mail communications. Phil Zimmermann developed PGP in 1991.

A modern PHP library that allows you to make resilient calls to external services
A modern PHP library that allows you to make resilient calls to external services

Resiliency, an implementation for resilient and modern PHP applications Main principles This library is compatible with PHP 7.4+. Installation compose

Simple analyse the traffic of your OctoberCMS-based website without relying on an external service.

Simple Analytics for OctoberCMS Simple analyse the traffic of your website without relying on an external service. The simple version of this plugin (

Curated list that contain code, snippets or examples without libraries or external packages for developers.

Awesome WordPress Developer Tips Curated list that contain very awesome and ready code, snippets or examples without libraries or external packages ma

Newsprint is a simple web application that will fetch the front page of a newspaper and display it on an eink display
Newsprint is a simple web application that will fetch the front page of a newspaper and display it on an eink display

Newsprint is a simple web application that will fetch the front page of a newspaper and display it on an eink display. The specific resolutions and sizes have been setup to work with a 32" eInk place & play display from Visionect but can be modified for other screen resolutions.

Collection of Google Maps API Web Services for Laravel

Collection of Google Maps API Web Services for Laravel Provides convenient way of setting up and making requests to Maps API from Laravel application.

Utilities to scan PHP code and generate class maps.

composer/class-map-generator Utilities to generate class maps and scan PHP code. Installation Install the latest version with: $ composer require comp

A privacy respecting free as in freedom meta search engine for Google and popular torrent sites
A privacy respecting free as in freedom meta search engine for Google and popular torrent sites

A privacy respecting free as in freedom meta search engine for Google and popular torrent sites

Open Source Smart Meter with focus on privacy - you remain the master of your data.
Open Source Smart Meter with focus on privacy - you remain the master of your data.

volkszaehler.org volkszaehler.org is a free smart meter implementation with focus on data privacy. Demo demo.volkszaehler.org Quickstart The easiest w

The modern, privacy-aware URL Shortener built in PHP.
The modern, privacy-aware URL Shortener built in PHP.

About UrlHum UrlHum is a modern, privacy-aware and fast URL Shortener built with PHP and the Laravel Framework. At the moment UrlHum is heavily under

A simple helper to generate and display pagination navigation links

Intro to CHocoCode Paginator Friendly PHP paginator to paginate everything This package introduces a different way of pagination handling. You can rea

Some Joomla! 4.x Web Services Api Examples and Experiments to raise the level of awareness of the huge potiental of Joomla! 4.x Web Services.

j4x-api-examples WHY? If you are a Joomla! developer or want to become a Joomla! developer there is a new resource for you The Official New Joomla! Ma

html-sanitizer is a library aiming at handling, cleaning and sanitizing HTML sent by external users

html-sanitizer html-sanitizer is a library aiming at handling, cleaning and sanitizing HTML sent by external users (who you cannot trust), allowing yo

AES 128 bit Encryption and Decryption algorithm excuted purely on PHP with no external libraries.
AES 128 bit Encryption and Decryption algorithm excuted purely on PHP with no external libraries.

AES128 Executed with PHP Advanced Encryption Standard (AES) is a specification for the encryption of electronic data established by the U.S National I

HTTP Requestor: Package for a client request that supports you to make an external service request easily and with fast usage.

HttpRequestor from Patienceman HTTP Requestor: Package for a client request that supports you to make an external service request easily and with fast

MediaWiki extension that allows embedding external content

MediaWiki extension that allows embedding external content, specified by URL, into your wiki pages.

This application gives you the ability to send a newsletter to multiple subscribers with use of SMTP or an external driver like Mailgun
This application gives you the ability to send a newsletter to multiple subscribers with use of SMTP or an external driver like Mailgun

Laravel Newsletter Laravel Newsletter is an open source project that can be used for sending newsletters to multiple subscribers, mailing lists, ... a

Comments
  • Error when running the example

    Error when running the example

    I tried to run this example.

    https://github.com/PrinsFrank/php-geo-svg#from-a-geojson-array

    Problem:

    Parse error: syntax error, unexpected '?', expecting function (T_FUNCTION) or const (T_CONST) in C:......vendor\prinsfrank\php-geo-svg\src\GeoSVG.php on line 24

    opened by JUREXT 2
Releases(v0.0.2)
  • v0.0.2(Nov 9, 2021)

    What's Changed

    • Add cylindrical equal area projections (Lambert, Behrmann, Smyth, Trystan, Hobo-Dyer, Gall-Peters, Balthasart and Tobler's world in a square) https://github.com/PrinsFrank/php-geo-svg/pull/6
    • Add MultiPoint support https://github.com/PrinsFrank/php-geo-svg/pull/4

    Development improvements

    • Reduce elementRenderer complexity https://github.com/PrinsFrank/php-geo-svg/pull/5
    • Remove element collapsing as it is unnecessarily complex https://github.com/PrinsFrank/php-geo-svg/pull/7
    • Add php-cs-fixer https://github.com/PrinsFrank/php-geo-svg/pull/1
    • Write missing unit tests https://github.com/PrinsFrank/php-geo-svg/pull/2 https://github.com/PrinsFrank/php-geo-svg/pull/3

    Full Changelog: https://github.com/PrinsFrank/php-geo-svg/compare/v0.0.1...v0.0.2

    Source code(tar.gz)
    Source code(zip)
  • v0.0.1(Oct 26, 2021)

Owner
Hi! I'm Frank Prins, A senior PHP developer specialized in Security.
null
New WordPress plugin to render a map with multiple markers using the open source maps provider OpenStreetMap

markers-on-openstreetmap New WordPress plugin to render a map with multiple markers using the open source maps provider OpenStreetMap A friend of mine

Oscar Alderete 2 Oct 25, 2021
A weather data tile generator for digital maps using the Web Mercator projection

Weather data tile generator for Web Mercator maps This is a weather data tile generator for digital maps, which is compatible with Leaflet and in gene

null 2 Aug 11, 2022
GeoSpatial integration on Laravel 5.2+ that supports MySQL and PostgreSQL.

Features GeoSpatial integration on Laravel 5.2+: Create geospatial columns using Schema and migrations Save and retrieve geospatial attributes using d

Eleven 47 Dec 22, 2022
Free database of geographic place names and corresponding geospatial data

FreeGeoDB Free database of geographic place names and corresponding geospatial data Entities airports cities countries (admin-0) lakes ports railroads

delight.im 1.6k Dec 15, 2022
Geo-related tools PHP 5.4+ library built atop Geocoder and React libraries

Geotools Geotools is a PHP geo-related library, built atop Geocoder and React libraries. Features Batch geocode & reverse geocoding request(s) in seri

The League of Extraordinary Packages 1.3k Dec 27, 2022
PHP library to easily get static image from French Cadastral Government map with markers and lines.

PHP French Cadastral Map Static API PHP library to easily get static image from French Cadastral Government map with markers and lines. Map source : c

Franck Alary 6 Nov 29, 2022
PHP library to easily get static image from OpenStreetMap (OSM) with markers and lines.

PHP OpenStreetMap Static API PHP library to easily get static image from OpenStreetMap with markers and lines. ✨ Supporting ⭐ Star this repository to

Franck Alary 34 Jan 2, 2023
Use: [i] to share item and name in hand, [coor] to share you current coordinates

General Now you can share your Coordinates and Item with Prefix Example if you type [i] in message, later it will be automatically replaced into the n

ItsRealNise 7 Oct 15, 2021
Official PHP library for IPinfo (IP geolocation and other types of IP data)

This is the official PHP client library for the IPinfo.io IP address API, allowing you to lookup your own IP address,

IPinfo 171 Jan 2, 2023