Web Map

Draw beautiful NZ boundary map image and tiles using PHP

I have been researching how to demonstrate some New Zealand statistical data in a geographical map service recently and drew a beautiful all NZ boundary map using PHP. Log here the whole process and I hope it is helpful for you in your project.

Firstly, download the original boundary data from the StatsNZ official website, I chose meshblock 2018 with the WGS 84(EPSG 4326) projection for my graphic.

Secondly, import the data into PostGIS database, you can use the ogr2ogr tool to import it, the reference command is here:

%ogr2ogr -f PostgreSQL "PG:user=postgres password=123456 host=localhost dbname=meshblock_2018_generalised" meshblock-2018-generalised.gpkg

After downloaded, you can access all data from the table “meshblock_2018_generalised” in PostGIS.

Thirdly, we need to get geographical data, all geo data is saved in the geometry field “geom”, we need to convert it to GeoJSON format then we can draw polygons. It is pretty easy to do it with the PostGIS’s internal funciton:

SELECT ST_ASGeoJSON(geom) AS geom FROM meshblock_2018_generalised

Finally, to draw these polygons in the png file, we need to

  • Convert every point from lon&lat coordinates(lat, lng) to point position(px, py) (Refer ESPG3857 Web Mecator)
  • Set the boundary axis start point position(sx, sy), end point position(ex, ey), image width, and image height
  • Calculate every point’s real position to the image, use the point position(px, py) minus start point(sx, sy), then you can get the point’s position in the image.
  • Draw the polygons using PHP function imagepolygon
  • Combine with the image width and height

This is the main part of the source code in PHP7+Laravel8

// Get all meshblock record from PostGIS
$cbs = Meshblock2018Generalised::getByBounds([[-180, -90], [180, 90]], true);

$tile_pixels = tile_to_pixel($zoom, [$x, $y], $image_size);

$coordinate_points = [];

foreach($cbs as $cb) {
     $geo_json = json_decode($cb['raw_geo_json']);
     $color = color_hex_to_rgb($cb['color']);

     foreach ($geo_json->coordinates as $k => $v) {
         $arr = [];
         foreach ($v[0] as $point) {
             $arr[] = (lon_to_pixel($zoom, $point[0], $image_size) - $tile_pixels[0] - 50 * $image_size / 256);
             $arr[] = (lat_to_pixel($zoom, $point[1], $image_size) - $tile_pixels[1] - 120 * $image_size / 256);
         $coordinate_points[] = ['color' => $color, 'points' => $arr];

// Create a transparent png image
$image = imagecreatetruecolor($image_size, $image_size);
$black = imagecolorallocate($image, 0, 0, 0);
imagecolortransparent($image, $black);

// Allocate a color for the polygon
$col_poly = imagecolorallocate($image, 88, 88, 88);

// Draw the polygon
foreach( $coordinate_points as $item){
    imagepolygon($image, $item['points'],count($item['points']) / 2,  $col_poly);

// Output the picture to the browser
header('Content-type: image/png');


The below graphic is generated in the border mode, I recommend you to open them in the new target, you will feel how beautiful these lines.

Part of New Zealand Meshblock Boundary
New Zealand Meshblock Boundary 4096

You also can fill it with colors like below or divide it into tiles per your requirement.

Welcome to leave a comment if you have any queries.