Monday, March 18, 2024

Convert North East Down (NED) to/from longitude,latitude and other various coordinate systems

The Koordinat2 web app (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html) has been upgraded to perform coordinate transformation between local coordinate system North East Down (NED) and many other coordinate systems including MGRS, geographic longitude/latitude, etc. 

This post shows the typical steps to perform the local NED conversion to other coordinate systems.

Define the NED local origin 

The local origin should be as near as possible to the area of interest. If it is too far away, the calculation rounding errors would result in erroneous converted coordinates.

  1. Use a browser to open the Koordinat2 web app url (https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html).

    The Koordinat2 web app home page appears.


  2. Click the hamburger menu icon on the top left.

    The Navigation drawer opens.



  3. Click the Settings item.

    The Settings dialog appears.


  4. Choose the item North East Down reference origin.

    The NED Reference Origin appears.


  5. In the dialog, enter the reference origin Longitude, Latitude, and Altitude. Click Save.

    The Reference Origin is saved.

Define NED as the Primary coordinates (input)

  1. In the Primary coordinates card, click the gear icon.

    The Primary coordinates selection appears.




  2. Select North East Down from the list or search for it by clicking the Search icon and typing in NED.


Define the Secondary coordinates (output)

  1. In the Secondary coordinates card, click the gear icon.

    The Secondary coordinates selection appears.



  2. Select a desired coordinate system e.g. WGS 84 from the list or search for it by clicking the Search icon and typing in  WGS 84.


Enter NED coordinates

  1. In the Primary coordinates card, type in the North, East, and Down values.

    The NED values are converted to the secondary coordinates. And a marker appears on the map.




Monday, February 12, 2024

Deserialize a list of JSON objects using json_annotation and json_serializable in Flutter

I could not find an example of how to read in a list of JSON objects from a file using the Flutter packages: json_annotation and json_serializable. After a while I figured the way to do it. This post shows an example of automating JSON de-serialization of a JSON file that looks like the following:

[
    {
        "code": "NED",
        "fullname": "North East Down (local)",
    },
    {
        "code": "ECEF",
        "fullname": "Earth Centered Earth Fixed (ECEF)",
    },    
    {
        "code": "LLA",
        "fullname": "WGS 84",
    }
]

Install the Flutter packages Json_serializable and Json_annotation

In a Terminal, type in the following:

$ cd /path/to/the/root/of/flutter/project/

$ flutter pub add json_annotation

$ flutter pub add json_serializable

Code the model class dart file

  1. In a text editor, create the model class dart file, e.g. cs_model.dart.

  2. Type in the following:

import 'package:json_annotation/json_annotation.dart';

// Part file to be auto generated
part 'cs_model.g.dart';

@JsonSerializable()
class APICoordSysQuery {

  // Factory method to load in the list of JSON objects
  factory APICoordSysQuery.fromJson(List<dynamic> parsedJson){
    List<APICoordSys> csList = List<APICoordSys>.empty();
    csList = parsedJson.map ( (e) => APICoordSys.fromJson(e)).toList();
    return APICoordSysQuery(
      csList: csList,
    );
  }
  
  // To be auto generated by the Json_serializable package
  Map<String, dynamic> toJson() => _$APICoordSysQueryToJson(this);

  @JsonKey(name: 'csList')
  List<APICoordSys> csList;

  APICoordSysQuery({
    required this.csList
  });
}

@JsonSerializable()
class APICoordSys {
  
  // Factory method to deserialize a JSON object to be auto generated
  factory APICoordSys.fromJson(Map<String, dynamic> json) =>
    _$APICoordSysFromJson(json);
  
  // Factory method to serialize a JSON Object to be auto generated
  Map<String, dynamic> toJson() => _$APICoordSysToJson(this);

  @JsonKey(name: 'code')
  String code;
  @JsonKey(name: 'fullname')
  String fullName;

  // Constructor
  APICoordSys({
    required this.code,
    required this.fullName
  });
}

Generate the .part file

  1. In a Terminal, run the following:

    $ cd /path/to/flutter/root/project/
    $ dart run build_runner build


    Processing messages appear and the cs_model.part.g.dart file is generated.

Now you should be able to load in a list of JSON objects in Flutter.

 

Monday, November 27, 2023

How I fixed the Android Studio current target and jvm target compatibility error

I encountered the following compilation error in Android Studio of one of my Android project about current target JVM compatibility (or incompatibility), as shown in the screenshot message listing below:

 

Execution failed for task ':app:compileDebugKotlin'.
> 'compileDebugJavaWithJavac' task (current target is 1.8) and 'compileDebugKotlin' task (current target is 17) jvm target compatibility should be set to the same Java version.
  Consider using JVM toolchain: https://kotl.in/gradle/jvm/toolchain

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.

I managed to fix the problem by ensuring the source and target are compatible, i.e. having the same Java version.

To fix the issue:

  1. In Android Studio, select File | Project Structure.

    The Project Structure dialog box appears.


  2. Click Gradle Settings.

    The Gradle dialog box appears.


  3. In the Use Gradle from combo box, choose 'gradle-wrapper.properties' file.

  4. In the Gradle JDK combo box, choose jbr-17 JetBrains Runtime version 17.

  5. Click OK to close all dialog boxes.

  6. In Android Studio, open the app's build.gradle file in the editor.


  7. Within the android construct, add in the compileOptions as shown in the listing below.

android {
    compileSdk 33

// ...etc...

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

From this point on, compiling the app should not throw upany  JVM target compatibility errors.

Monday, November 13, 2023

Using Koordinat2 webapp to convert between SVY21 and geographical coordinates

This is a follow up to the original post https://dominoc925.blogspot.com/2021/02/koordinat2-webapp-for-geo-coordinates.html, which describes a generic coordinate conversion steps. This post shows how to specifically use the Koordinat2 webapp https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html to convert from WGS 84 latitude, longitude coordinates to SVY21 projected easting and northing coordinates, and vice versa.

Select the WGS 84 lat, lon as the source coordinate system

  1. Using a modern browser, open the url https://dominoc925-pages.appspot.com/webapp/koordinat2/default.html.

    The Koordinat2 web app appears in the browser.


  2. In the Input Coordinates card, click the gears icon labeled 1 in the screen above.

    The Select Source Coordinate System dialog appears.


  3. In the Search field, type in a partial string for the WGS84 latitude, longitude system, e.g. "4326" or "WGS 84".

    A list of matches appear.



  4. Click the WGS 84 list item.

    The WGS 84 geographic coordinate system is selected.


Select the destination SVY21 coordinate system

  1. In the Output Coordinates card, click the gear icon (labeled 2 in the screen below).




     

    The Select Destination Coordinate System dialog appears.


  2.  In the Search field, type in a partial string for SVY21, e.g. "3414" or "SVY21".

    A list of matching coordinate systems appears.


  3. Click the SVY21 / Singapore TM list item.

    The destination coordinate system is selected.


Performing coordinate conversions from latitude, longitude to SVY21

  1. In the Input Coordinates card, enter the Longitude and Latitude values.

    The Longitude/Latitude coordinates are dynamically converted and displayed in the Output Coordinates card.

  2. Optional. If you wish to see the location of the coordinates on the map, click the marker icon as shown below.



    The location is marked by a marker on the map.


Performing coordinate conversion from SVY21 to WGS84 latitude, longitude

If you wish to perform the reverse conversion, simply repeat the previous steps but reverse the coordinate system selections.

 

 

Monday, October 23, 2023

React JS Material UI icons for Mastodon and Blogger

Other than the Linkedin icon, I couldn't find the Mastodon and Blogger React Material UI (MUI) icons in the React @mui/icons-material library so I had to make my own with Inkscape and a good old text editor. The results are shown in the screenshot below.

 Here is the MastodonIcon.js code for the Mastodon icon:

import * as React from 'react';
import SvgIcon from '@mui/material/SvgIcon';

export default function MastodonIcon() {
  return (
    <SvgIcon>
      <svg className="mastodon" width="16" height="16" fill="currentColor" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
        <path d="m10.751 11.617c1.7387-0.20699 3.2514-1.2721 3.4412-2.2449 0.30012-1.5334 0.27598-3.7421 0.27598-3.7421 0-2.9926-1.9715-3.8706-1.9715-3.8706-0.99355-0.45451-2.7002-0.6451-4.4734-0.65976h-0.043122c-1.774 0.014661-3.48 0.20526-4.4734 0.65976 0 0-1.9706 0.87709-1.9706 3.8706l-0.00173 0.57093c-0.00345 0.55195-0.00603 1.1643 0.0095 1.8033 0.071582 2.9271 0.53988 5.8128 3.26 6.5286 1.2539 0.33031 2.3311 0.39931 3.1988 0.35188 1.5722-0.08625 2.4554-0.55799 2.4554-0.55799l-0.05176-1.1358s-1.1237 0.3536-2.3864 0.31047c-1.2505-0.04312-2.57-0.13454-2.7728-1.6628a3.1169 3.1169 0 0 1-0.028456-0.42777s1.2281 0.2984 2.784 0.36912c0.95126 0.04312 1.843-0.0552 2.7495-0.163zm1.3911-2.1302h-1.4429v-3.5188c0-0.74083-0.31393-1.1168-0.94091-1.1168-0.69339 0-1.041 0.44587-1.041 1.329v1.9258h-1.4351v-1.9266c0-0.88313-0.34756-1.329-1.041-1.329-0.62699 0-0.94091 0.37602-0.94091 1.1177v3.5179h-1.4429v-3.6239c0-0.74083 0.18973-1.329 0.56921-1.7646 0.39327-0.43553 0.90728-0.6589 1.5463-0.6589 0.73824 0 1.2971 0.28288 1.6671 0.84777l0.35964 0.5994 0.35964-0.5994c0.36998-0.5649 0.92884-0.84777 1.668-0.84777 0.6382 0 1.1522 0.22337 1.5446 0.6589 0.38119 0.43553 0.57007 1.0237 0.57007 1.7646z" strokeWidth=".86244" />
      </svg>
    </SvgIcon>
  );
}

And here is the BloggerIcon.js code for the Blogger icon:

import * as React from 'react';
import SvgIcon from '@mui/material/SvgIcon';

export default function SvgIconChildren() {
  return (
    <SvgIcon>
      <svg width="50.664mm" height="50.575mm" version="1.1" viewBox="0 0 179.52 179.2" xmlns="http://www.w3.org/2001/svg">
        <defs>
          <clipPath id="clipPath8">
            <path d="m111.47 137.84c6.8936-0.96075 12.296-3.7837 17.364-9.0736 3.6662-3.8268 5.9611-7.9689 7.4609-13.466 0.62305-2.2837 0.67529-3.3956 0.78946-16.804 0.0863-10.12 0.0143-14.86-0.24367-16.056-0.37384-1.7344-1.4336-3.345-2.6427-4.0165-0.37209-0.20664-2.7561-0.47002-5.2978-0.58526-4.2591-0.19314-4.7356-0.278-6.08-1.0829-2.1324-1.2768-2.7197-2.6555-2.7255-6.3987-0.0111-7.152-2.9243-13.792-8.6802-19.785-4.1006-4.2694-8.6751-7.1592-13.896-8.7785-1.2498-0.38765-4.0484-0.51957-13.422-0.63274-14.708-0.17757-17.973 0.13047-22.98 2.1682-9.2314 3.7568-15.864 11.674-18.284 21.824-0.45444 1.9064-0.54266 4.9618-0.65 22.513-0.13448 21.988 0.01386 25.217 1.3586 29.575 1.111 3.6002 2.232 5.8063 4.5414 8.9376 4.3994 5.9652 10.993 10.273 17.585 11.49 3.137 0.57911 41.84 0.72399 45.804 0.17161z" display="none" fill="#fff" strokeWidth=".86401" />
            <path className="clip" d="m8.0345 6.3499h163.45v166.5h-163.45zm103.44 131.49c6.8936-0.96075 12.296-3.7837 17.364-9.0736 3.6662-3.8268 5.9611-7.9689 7.4609-13.466 0.62305-2.2837 0.67529-3.3956 0.78946-16.804 0.0863-10.12 0.0143-14.86-0.24367-16.056-0.37384-1.7344-1.4336-3.345-2.6427-4.0165-0.37209-0.20664-2.7561-0.47002-5.2978-0.58526-4.2591-0.19314-4.7356-0.278-6.08-1.0829-2.1324-1.2768-2.7197-2.6555-2.7255-6.3987-0.0111-7.152-2.9243-13.792-8.6802-19.785-4.1006-4.2694-8.6751-7.1592-13.896-8.7785-1.2498-0.38765-4.0484-0.51957-13.422-0.63274-14.708-0.17757-17.973 0.13047-22.98 2.1682-9.2314 3.7568-15.864 11.674-18.284 21.824-0.45444 1.9064-0.54266 4.9618-0.65 22.513-0.13448 21.988 0.01386 25.217 1.3586 29.575 1.111 3.6002 2.232 5.8063 4.5414 8.9376 4.3994 5.9652 10.993 10.273 17.585 11.49 3.137 0.57911 41.84 0.72399 45.804 0.17161z" strokeWidth=".86401" />
          </clipPath>
        </defs>
        <path d="m-82.995 87.838v-171.9h1020v343.8h-1020v-171.9z" fill="none" />
        <g transform="matrix(.92141 0 0 .92141 7.0545 7.0421)" fill="#fff" strokeWidth=".86401">
          <path d="m30.568 167.24c-2.8716-0.77176-5.3495-1.9071-7.6348-3.4982-1.9292-1.3431-4.7488-4.1198-5.8212-5.7323-1.3096-1.9694-2.815-5.342-3.4285-7.6809-0.62576-2.3858-0.6359-3.336-0.64713-60.605-0.01116-56.98 0.0015-58.233 0.61488-60.681 2.169-8.6596 8.8857-15.248 17.548-17.214 2.4907-0.56514 113.75-0.66398 116.44-0.10343 7.2734 1.5175 12.991 5.979 16.299 12.719 2.6303 5.3583 2.3951-0.53816 2.5156 63.081 0.0767 40.479 6e-3 57.473-0.24689 59.59-1.1845 9.9072-7.8732 17.592-17.498 20.103-2.462 0.64225-3.3184 0.65142-59.215 0.63395-54.046-0.0166-56.821-0.0454-58.928-0.61194z" clipPath="url(#clipPath8)" />
          <path d="m70.797 77.476c-3.5242-0.9925-4.841-6.1585-2.2508-8.8303 1.6555-1.7077 2.1132-1.7727 12.476-1.7727 9.3029 0 9.6156 0.02079 10.982 0.72599 1.9748 1.0193 2.8328 2.4564 2.8328 4.7444 0 2.0665-0.80586 3.5146-2.6034 4.6784-0.96518 0.62486-1.542 0.66375-10.657 0.71844-5.6283 0.0338-10.112-0.07615-10.78-0.26416zm-0.44157 34.766c-1.5128-0.67346-2.9215-2.5442-3.165-4.203-0.23192-1.5801 0.54532-3.7524 1.7367-4.854 1.5018-1.3886 2.161-1.435 20.63-1.4498 18.999-0.0152 18.9-0.0234 20.701 1.6952 2.5446 2.4274 2.0078 6.749-1.0585 8.5234l-3.151 0.52354-16.423 0.19641c-14.431 0.17258-18.519-0.0973-19.271-0.43172z" />
        </g>
      </svg>
    </SvgIcon>
  );
}

Hope these help somebody.

Monday, June 5, 2023

How to sync the time between 2 Ubuntu systems on an isolated network

I wanted to match the times between two systems on an isolated network running Ubuntu 22. This can be done using chrony (https://chrony.tuxfamily.org) on the two systems - one system serves as the local time server to the other client system. 

Setting up the Server

  1. Optional. If chrony is not installed, run the following command in the Terminal to install it.

    $ sudo apt install chrony

  2. Using a text editor, add the following lines to the file /etc/chrony/chrony.conf.


    local stratum 8
    allow xxx.xxx.xxx.xxx


    Note 1: The keyword local tells chrony to server isolated networks.
    Note 2: The allow keyword specifies the IP address of the client to server


  3. Restart the chrony service using the following command. Or you can reboot.

    $ sudo systemctl restart chronyd

Setting up the Client

  1.  Optional. If chrony is not installed, run the following command in the Terminal to install it.

    $ sudo apt install chrony

  2. Using a text editor, add the following to the file /etc/chrony/chrony.conf.

    server yyy.yyy.yyy.yyy minpoll 0 maxpoll 5 maxdelay 0.1

  3. Restart the chrony service using the following command. Or reboot.

    $ sudo systemctl restart chronyd

  4. To verify whether the syncing is working, the following command can be used.

    $ chronyc sources -v



    Note: If the sync is successful, the MS column should be showing the symbols ^* for the IP address of the server entry.


 

Monday, April 3, 2023

How to create a Arm64 Ubuntu virtual machine using Virt-Manager

I wanted to run Ubuntu on an Arm64/Aarch64 virtual machine on a host Intel Linux computer for the longest time for compiling binaries for Raspberry Pis and other SBC boards. I finally figured out how to do it with virt-manager, and it is quite simple as clicking and selecting options on the virt-manager graphical user interface. The instructions below show how it is done.

Download and install prerequisites

  1. In Ubuntu, open up a Terminal and enter the following commands to install the prerequisite software.

    $ sudo apt install qemu-kvm \
    libvirt-daemon-system \
    libvirt-clients \
    bridge-utils \
    virt-manager \
    qemu-system-arm \
    qemu-efi-aarch64 \
    qemu-utils
    

  2. Open up an Internet browser. Download an Ubuntu Arm64 image to install. For example: https://cdimage.ubuntu.com/releases/22.04/release/ubuntu-22.04.2-live-server-arm64.iso

 Create the virtual machine

  1. In Ubuntu, click the Virtual Machine Manager icon.

    The Virtual Machine Manager application appears.


  2. Select File | Create New Virtual Machine.

    The New VM dialog box pops up.


  3. Choose Local install media (ISO image or CDROM).

  4. In the Architecture options, choose aarch64 architecture. Click Forward.

    Step 2 of 5 pages appears.

  5. In the Choose ISO or CDROM install media field, choose the iso image downloaded previously.



  6. Click Forward. If necessary, change the memory size and the number of CPUs.

    Step 3 of 5 pages appear.


  7. Click Forward. If necessary, change the disk image size to a suitable size.

    Step 4 of 5 appears.


  8. Click Forward.

    Step 5 of 5 appears.


  9. Optional. In the Name field, type in a desired name.

  10. Toggle On the Customize configuration before install field.




  11. Click Finish.

    The 'vm name' on QEMU/KVM dialog box appears.



  12. In the Hypervisor Details group, change the Firmware to UEFI aarch64.


  13. Click Apply. Then click the Begin Installation button and follow the prompts to complete the installation.

    The installation begins...