Categories
Web Development

How to use Google Typescript Style Guide in a ReactJS project

gts is Google’s TypeScript style guide, and the configuration for formatter, linter, and automatic code fixer. No lint rules to edit, no configuration to update, no more bike shedding over syntax. there are a few configurations to use it in a ReactJS project and I logged here:

1. Create a new React project with the TypeScript template

%npx create-react-app client-demo --template typescript
%cd client-demo
%yarn

2. Add and initiate gts into the project

Before adding gts, please backup your tsconfig.json file because gts will generate a new one overwriting it.

Execute the below command, it will prompt if you want to overwrite the existing “tsconfig.json”, just select “y” here.

%npx gts init

We have installed ReactJS and gts in our project, however, if you try starting the project with “yarn start” now, you will encounter the below error due to the eslint setting conflicts between reactjs and gts, we need to do some extra work to solve it.

3. Restore tsconfig.json changed by gts

The tsconfig.json file overwritten by gts is like this:

{
  "extends": "./node_modules/gts/tsconfig-google.json",
  "compilerOptions": {
    "rootDir": ".",
    "outDir": "build"
  },
  "include": [
    "src/**/*.ts",
    "test/**/*.ts"
  ]
}

Replace the above with our backup file, just keep the first “extends” line,

{
  "extends": "./node_modules/gts/tsconfig-google.json",
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src"
  ]
}

4. Eslint settings

Install required eslint plugins:

%yarn add -D eslint-plugin-react eslint-plugin-node eslint-plugin-prettier

Update eslintrc.json:

/* This is original created by gts */
{
  "extends": "./node_modules/gts/"
}

To:

{
  "extends": [
    "./node_modules/gts/",
    "plugin:react/recommended"
  ],
  "env": {
    "browser": true,
    "jest": true
  }
}

Now, you have completed all the above four-step settings and can run ReactJS successfully.


There is one more optional setting I would recommend is adding husky pre-commit check so the system will execute gts check automatically when committing your code every time.

1. Install husky

npx husky-init && yarn

2. Edit “./husky/pre-commit” file

Replace "npm test" as "./node_modules/.bin/gts check"

All done, now the system will do “gts check” before committing your code.

Thanks for reading, please comment and let me know if you have any questions.

Categories
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');

imagepng($image);
imagedestroy($image);
exit;

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.

Categories
Web Map

Use d3js to show the map of New Zealand regional council boundaries

The article introduces how to use d3js showing the map of New Zealand regional council boundaries.

You can get the geometry data of NZ regional council from the NZ official website: https://datafinder.stats.govt.nz/layer/92197-meshblock-2018-generalised, remember to download the WGS84(EPSG:4326) projection format.

I downloaded the original data from the above URL and converted it to the Topo-JSON type as below.

Move the mouse to the map to get the regional councils’ basic info:

The source code is here:

<script src="https://d3js.org/d3.v6.min.js"></script>
<script src="https://unpkg.com/topojson-client@3"></script>
<div id="map-nz-regional-council"></div>
<script>


        window.addEventListener('load', async () => {
            await init_map();
        })

        async function init_map(){
            let width = 960
                , height = 960
                , centered;

            const file_name_topojson = '/wp-content/uploads/2021/03/regional_council_2018_generalised.topojson.json';
            const topo_json = await d3.json(file_name_topojson);

            const zoom = d3.zoom()
                .scaleExtent([1, 4])
                .on('zoom', zoomed);

            function zoomed(event) {
                const {transform} = event;
                g.attr("transform", transform);
                g.attr("stroke-width", 1 / transform.k);
            }

            const projection =
                    d3.geoMercator()
                    .scale(2000)
                    .center([176, -38]);

            const path = d3.geoPath(projection);

            const svg = d3.select('#map-nz-regional-council').append('svg')
                .attr('width', width)
                .attr('height', height)
                .attr('fill', '#fff');

            svg.append('rect')
                .attr('class', 'background')
                .attr('width', width)
                .attr('height', height)
                .on('click', clicked);

            svg.call(zoom);

            const g = svg.append('g');

            const textLayer = g.append('g');

            const mapLayer = g.append('g')
                .classed('map-layer', true);

            // const features = geo_json.features;

            // Draw each provinces as a path
            mapLayer.selectAll('path')
                // .data(geo_json.features)
                .data(topojson.feature(topo_json, topo_json.objects['regional_council_2018_generalised']).features)
                .enter()
                .append('path')
                .attr('d', path)
                .attr('vector-effect', 'non-scaling-stroke')
                .style('stroke', '#000000')
                // .style('fill', fillFn)
                .on('mouseover', mouseover)
                .on('mouseout', mouseout)
                .on('click', clicked)
                ;

            function clicked(d){
                const pointer = d3.pointer(d);
                const res = projection(pointer);
                showInfo(d3.select(this).data());

            }

            function showInfo(data){
                textLayer.selectAll('text').remove();
                if(data && data[0]){
                    Object.keys(data[0]['properties']).map((k, i) => {
                        textLayer.append('text')
                            .attr('x', 0)
                            .attr('y', i * 14)
                            .attr('dy', '1.2em')
                            .style('fill', '#000')
                            .text( k + " => " + data[0]['properties'][k] );
                    });
                }
            }

            function mouseover(d){
                // Highlight hovered province
                d3.select(this).style('fill', 'orange');
                showInfo(d3.select(this).data());
            }

            function mouseout(d){
                // Reset province color
                mapLayer.selectAll('path')
                    .style('fill', function(d){return centered && d===centered ? '#D5708B' : '#ffffff';});
            }

        }

</script>
Categories
Web Development

Build Magento 2.4.2 Community test environment using nginx1.19+php7.3+mysql8 on MacOS

The article logs my process of using Nginx1.19 + php7.3 + MySQL8 to build the test environment of Magento on my MacBook(macOS Big Sur 11.2.1), I hope it is helpful for you.

Before starting the installation, you need homebrew installed on the computer.

Nginx Part:

Firstly, install Nginx and start the service

% brew install nginx
% brew services start nginx

MySQL Part:

Secondly, install MySQL8, start the service and create the database

% brew install mysql
% brew services start mysql

Edit my.cnf to run MySQL8 with native password authentication

% vi /usr/local/etc/my.cnf 

#add the below line in the section [mysqld]
default-authentication-plugin=mysql_native_password

# If my.cnf file doesn't exist in the directory '/usr/local/etc'
# use the following commands to find out where it is
# ---------------------------------------------
# % sudo /usr/libexec/locate.updatedb
# #wait a few minutes for it to finish
# locate my.cnf
# --------------------------------------------- 

Save then restart MySQL service

% brew services restart mysql

Change MySQL password

% mysql -uroot
% mysql> use mysql;
% mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
% mysql> flush privileges;
% mysql> exit;

Create new Magento database:

% mysql -uroot -p123456
% mysql> create database magento;
% mysql> exit;

Mysql has been installed successfully and configured properly. Now let us install PHP.

PHP Part:

The default PHP version is PHP8 by brew however Magento2 doesn’t support it so we need to install a downgraded PHP7.3 version and start the service for php-fpm mode which will be occupying port 9000.

% brew install php@7.3
% brew link --force php@7.3
% brew services start php@7.3

After installed, open a new terminal to confirm the latest php7.3 has been installed:

There are some configures you need to set in php.ini

% vi /usr/local/etc/php/7.3/php.ini

then update following settings:

memory_limit=2G
date.timezone=Pacific/Auckland
zlib.output_compression = On

#For test purpose only
max_execution_time = 86400
upload_max_filesize = 2G 
post_max_size = 2G
max_input_vars = 86400 
max_input_time = 86400

remember to restart PHP services after updated php.ini

% brew services restart php@7.3

Install composer as the PHP package management tool

% brew install composer

Magento Part:

Install elasticsearch and start the service which relied on Magento.

% brew install elasticsearch
% brew services start elasticsearch

Download Magneto source code to a new folder, I name it as “magento-community” and you can use any name.

composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition magento-community

You will be asked to provide the name and password for downloading from repo.magento.com, access https://marketplace.magento.com/ , register a new user or login, then, click “My Profile” on your username at the right top of the page.


Click “Access Keys” to go to the Access Keys page, then create a new access key, and use Public Key as username and Private Key as password, then you should be able to download Magento Community Version, my downloaded version is Magento 2.4.2.

After downloading successfully, go to the Magento folder,

Install Magento:

% php bin/magento setup:install --base-url=http://127.0.0.1:8001/ --db-host=127.0.0.1 --db-name=magento --db-user=root --db-password=123456 --admin-firstname=admin --admin-lastname=admin --admin-email=ukalpa@gmail.com --admin-user=admin --admin-password=admin123456 --language=en_AU --currency=NZD --timezone=Pacific/Auckland --use-rewrites=1

Starting Magento installation:
File permissions check...
[Progress: 1 / 1314]
Required extensions check...
[Progress: 2 / 1314]
Enabling Maintenance Mode...
[Progress: 3 / 1314]
Installing deployment configuration...
......
[Progress: 1311 / 1314]
Disabling Maintenance Mode:
[Progress: 1312 / 1314]
Post installation file permissions check...
For security, remove write permissions from these directories: '/Users/ukalpa/Projects/php/magento/magento-community/app/etc'
[Progress: 1313 / 1314]
Write installation date...
[Progress: 1314 / 1314]
[SUCCESS]: Magento installation complete.
[SUCCESS]: Magento Admin URI: /admin_15owhz
Nothing to import.
ukalpa@Shuis-MacBook-Pro magento-community % 

Magento has been installed successfully, remember to turn off two-step authentication or you will get an email notification to confirm login.

php bin/magento module:disable Magento_TwoFactorAuth

now let us configure Nginx.

Configure Nginx to support PHP

% vi /usr/local/etc/nginx/servers/magento-community 
--------------------------------------------------
# add the following content in the file
# remeber to change the folder path to your path

upstream fastcgi_backend {
     server  127.0.0.1:9000;
 }

server {
        listen 8001;
        server_name 127.0.0.1;
        set $MAGE_ROOT /Users/ukalpa/Projects/php/magento/magento-community;
        set $MAGE_DEBUG_SHOW_ARGS 0;
        include /Users/ukalpa/Projects/php/magento/magento-community/nginx.conf.sample;

}

#save and exit
-------------------------------------------------
 
% brew services restart nginx

Now Magento has been installed successfully on Macbook, you can access it at http://127.0.0.1:8001

Categories
Web Map

Build your self-host world map – Download data and render tiles on your mac

This article demonstrates how to download and serve a city’s map on your Macbook.

1. Download openstreetmap data

Firstly we need to get the OpenStreetMap data for rendering tiles, you can download it from various places, refer “LearnOSM – Getting OSM Data” to get the specific data you want. I downloaded Auckland city data from BBBike.org

2. If there is no Docker installed on your Mac, download and install it.

3. Setting up the tiles server:

1). Create new volume in Docker:

% docker volume create openstreetmap-data

2). Use openstreetmap-tile-server to build the server, start importing the “.pbf” file into PostgreSQL by running a container and mounting the file as /data.osm.pbf. Remember to use the physical file path:

% docker run -v /Users/ukalpa/Downloads/Auckland.osm.pbf:/data.osm.pbf -v openstreetmap-data:/var/lib/postgresql/12/main overv/openstreetmap-tile-server import

3). Running the server

% docker run -p 8301:80 -v openstreetmap-data:/var/lib/postgresql/12/main -d overv/openstreetmap-tile-server run

Now, the tiles server has been installed successfully, access http://localhost:8301 and you will see the Auckland map details:

Auckland City Summary
Auckland Downtown
Queen Street

For more details about openstreetmap-tile-server, please refer: https://github.com/Overv/openstreetmap-tile-server

Categories
Web Map

Build your self-host world map – Install mapnik & python-mapnik on MacOS

Mapnik is a mapping toolkit for developing mapping applications. it can generate map tiles with specific requirements based on the data in PostGIS generated by OSM. This article introduces how to install it and the python library “python-mapnik” for Python3 on Mac OS.

First, make sure you have installed homebrew on your Macbook. And it is updated:

%brew update

Then, install mapnik:

%brew install mapnik

After installed, Execute “brew info mapnik” to check the installed details, you can see the version 3.1.0 has been installed successfully as below:

Now, let us start to install “python-mapnik”:

First, clone the “python-mapnik” source code to the local directory and switch its version to “v3.0.x”, the reason for switching the version is because the master branch is not compatible with the previous installed mapnik’s version.

%git clone https://github.com/mapnik/python-mapnik.git
%cd python-mapnik
%git checkout v3.0.x

You may encounter the issue: “library not found boost_python” when installing python-mapnik, to solve it, we need to install “boost-python3” firstly.

%brew install boost-python3

After installed “boost-python3”, execute the command

%ls -l /usr/local/lib/libboost_python*

to check the installed version, you can see libboost_python39 has been installed on my computer.

Now, start to install the “python-mapnik” library:

// "39" should be the install version as same as above
%export BOOST_PYTHON_LIB=boost_python39
%python3 setup.py install

After the installation is finished, you should see the below result.

You can execute the following commands to test if it is installed successfully:

%git submodule update —init
%python3 setup.py test

Now, we have installed “mapnik” and “python-mapnik” successfully in the system, you can execute a simple python code to get where the library is stored as below, please notice that you might need to restart your terminal instance for Python to identify the library.

Welcome to comment if you have any questions.

Categories
Web Map

Build your self-host world map – Download and import map data into PostGIS

This article logs the process of building my on-premise web map and talks about I use OpenStreetMap to download world map data and import it into PostGIS for late use.

What is OpenStreetMap(OSM)

OpenStreetMap(OSM) is a collaborative project to create a free editable map of the world. The geodata underlying the map is considered the primary output of the project. The creation and growth of OSM have been motivated by restrictions on the use or availability of map data across much of the world, and the advent of inexpensive portable satellite navigation devices.

Where to download map data

You can download OSM data from the official website https://planet.openstreetmap.org , or from a mirror website https://wiki.openstreetmap.org/wiki/Planet.osm#Downloading , and I suggest you download a small part such as a city for testing purpose. I downloaded the New Zealand data as below for testing:

From: http://download.geofabrik.de/

Pre-requisites

We need to install the importing tool “osm2pgsql” and “PostGIS” on the computer. For installing “osm2pgsql”, please refer: https://osm2pgsql.org/doc/install.html, for PostGIS, you can refer to my previous post “Install PostgreSQL 13 with PostGIS and PgAdmin 4 with Docker on Mac” if your laptop is MacBook.

Let’s start the process

  1. Create new user “osmuser” without any database creation privileges in PostgreSQL
    CREATE ROLE osmuser WITH LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE INHERIT NOREPLICATION CONNECTION LIMIT -1 PASSWORD ‘123456’; COMMENT ON ROLE osmuser IS ‘User for OpenStreetMap(OSM)’;
  2. Create the new database “osm” in PostgreSQL
    CREATE DATABASE osm WITH OWNER = osmuser ENCODING = ‘UTF8’ CONNECTION LIMIT = -1;
  3. Add the extensions “postgis” and “hstore” in the database
    CREATE EXTENSION postgis; CREATE EXTENSION hstore;
  4. Import it into the database:
    osm2pgsql new-zealand-latest.osm.pbf -d osm -H localhost -U postgres -W

After executed successfully, you can see 4 tables have been created and OSM data have been inserted into the database.

Categories
Web Development

A console.log issue in Google Chrome

Found an interesting issue in Google Chrome today and have reported it to bugs.chromium.org.

The issue relates to change and print object value in a loop, you can reproduce it by executing the following code in Chrome Console.

var book = {info:{}};
for(var i = 0; i < 3; i++){
    book.info.id = i;
    console.log(book);
}

The expected result “id” in the object book should be 0, 1, 2 however it prints 2, 2, 2 as below. The code executes as expected in Safari.

I assume the issue is encountered by the cache rule of speeding up console.log only checks whether the top level of the variable is changed in a loop so caused the issue.

UPDATE:

Got the reply from Chromium that they won’t fix it:

Working as intended. If you hover over the [ i ] icon for more information, it will tell you that the variable content was evaluated at the point when you expanded the log entry.

Categories
Web Map

Install PostgreSQL 13 with PostGIS and PgAdmin 4 with Docker on Mac

PostGIS is the best Geospatial DB solution for applications besides the geographic world. This article is a guide for building the environment of PostgreSQL 13 with PostGIS and PgAdmin 4 using Docker on Mac. I assume you have already installed Docker on your Mac so just introduce how to install PostGIS and PgAdmin 4.

  1. Create a new Docker network
$docker network create --driver bridge postgis-network

2. Create the new PostGIS Docker container with the above network

docker run --name postgis -p 5432:5432 -e POSTGRES_PASSWORD=123456 --hostname postgis --network postgis-network --detach postgis/postgis

3. Create the new PgAdmin4 Docker container with the above network

docker run --name pgadmin4 -p 5050:80 -e "PGADMIN_DEFAULT_EMAIL=ukalpa@gmail.com" -e "PGADMIN_DEFAULT_PASSWORD=123456" --hostname pgadmin4 --network postgis-network --detach dpage/pgadmin4

4. Open the installed PgAdmin in the browser, then login and connect the PostGIS server.

Execute the query “SELECT * FROM pg_available_extensions;”, we can then available PostGIS extensions in the list.

Open PostGIS extension in the specific database

CREATE EXTENSION postgis;
CREATE EXTENSION postgis_raster;
-- Enable Topology
CREATE EXTENSION postgis_topology;
-- Enable PostGIS Advanced 3D
-- and other geoprocessing algorithms
-- sfcgal not available with all distributions
CREATE EXTENSION postgis_sfcgal;
-- fuzzy matching needed for Tiger
CREATE EXTENSION fuzzystrmatch;
-- rule based standardizer
CREATE EXTENSION address_standardizer;
-- example rule data set
CREATE EXTENSION address_standardizer_data_us;
-- Enable US Tiger Geocoder
CREATE EXTENSION postgis_tiger_geocoder;

Now, you have successfully opened PostGIS in the database, you can execute the following query to check

SELECT postgis_full_version();

Categories
Web Development

The best solution to build your website for developers

Review the process of building this website and I hope this article could be helpful for you to save money in building your website.

I know there already have a lot of websites providing easy solutions for creating your own website but still some reasons you want to create by yourself because of saving money or easily customization solutions or even just because you are a programmer. I write down my solution I think the best for your reference.

My solution is using the free Linux operation system and opensource software WordPress to build a website and deploying in Google Cloud which I estimate will just cost no more than NZD$100 annual fee ideally fulfills all your various requirements in your website’s early stage. Google Cloud has a low/free host plan calling “f1-micro” in GCE(Google Compute Engine) available in its some zones and you can spend tiny coins in extra disk space and a static IP or snap services to improve the flexibility and reliability of your website. Let’s talk about the details of my plan:

DOMAIN

I applied my domain in Google Domains and it costs me NZD$22 a year, you are free to hide your registration info which is a charged service in GoDaddy. Another advantage is you can easily set SSL certificates in Google Cloud Platform with Google Domains.

CLOUD HOST

As I mentioned above, I chose the plan “f1-micro (1 vCPU, 0.6 GB memory)” is free and an extra 10G hard disk and a static IP which only costs you just a few bucks per month.

OS AND SOFTWARE

Choose Ubuntu 20.04 as the operating system because it’s free, stable, and easily maintaining, Nginx as the webserver, WordPress as the website tool.

SSL CERTIFICATE

Use Let’s Encrypt‘s free SSL solution to apply SLL certificate by installing certbot on your server. there are very detailed documents for various operating systems in the certbot’s website. the whole process is as easy as a piece of cake.

OTHERS

WordPress has a lot of free plugins for your use. Google as well, for example, you can use Google Analytics to analyze your website’s visits, use Google Firebase for your site’s authentication solutions.

In summary

The solution will probably just cost you between $20 to $40 a year, then, you have a whole virtual machine, domain with SSL certificate, and website, so I think it’s the best for all developers, please comment if you have any questions or a better solution.