diff --git a/.clang-format-ignore b/.clang-format-ignore
deleted file mode 100644
index 5e71f5bf..00000000
--- a/.clang-format-ignore
+++ /dev/null
@@ -1 +0,0 @@
-CMakePresets.json
diff --git a/.clang-tidy b/.clang-tidy
deleted file mode 100644
index 0ca21696..00000000
--- a/.clang-tidy
+++ /dev/null
@@ -1,19 +0,0 @@
-Checks:
-  - '-*'
-  - 'bugprone-*'
-  - 'clang-analyzer-*'
-  - 'cppcoreguidelines-*'
-  - 'misc-*'
-  - 'modernize-*'
-  - 'performance-*'
-  - '-bugprone-easily-swappable-parameters'
-  - '-cppcoreguidelines-avoid-do-while'
-  - '-cppcoreguidelines-avoid-non-const-global-variables'
-  - '-cppcoreguidelines-pro-type-reinterpret-cast'
-  - '-cppcoreguidelines-pro-type-union-access'
-  - '-misc-include-cleaner'
-  - '-misc-non-private-member-variables-in-classes'
-  - '-misc-use-anonymous-namespace'
-  - '-modernize-return-braced-init-list'
-  - '-modernize-use-trailing-return-type'
-FormatStyle: 'file'
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e3033807..ba82d35a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,7 +11,7 @@ on:
 
 concurrency:
   # Cancel in-progress jobs for the same pull request
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+  group: ${{ github.head_ref || github.run_id }}
   cancel-in-progress: true
 
 jobs:
@@ -20,130 +20,61 @@ jobs:
       fail-fast: false
       matrix:
         include:
-          - name: windows_msvc2022_x64
+          - name: win64_msvc2022
             os: windows-2022
             build_type: Release
             env_cc: ''
             env_cxx: ''
             compiler: msvc
-            cppflags: ''
-            ldflags: ''
             msvc_arch: x64
             msvc_version: 2022
-            qt_version: 6.9.2
-            qt_arch_aqt: win64_msvc2022_64
-            qt_arch_dir: msvc2022_64
+            qt_version: 6.7.2
+            qt_arch_aqt: win64_msvc2019_64
+            qt_arch_dir: msvc2019_64
             qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport
             qt_tools: ''
+            conan_arch: x86_64
+            conan_compiler: Visual Studio
+            conan_compiler_version: 17
+            conan_compiler_runtime: --settings compiler.runtime=MD
             conan_package_manager: ''
-            conan_profile: scwx-windows_msvc2022_x64
-            appimage_arch: ''
             artifact_suffix: windows-x64
-          - name: linux_gcc_x64
+          - name: linux64_gcc
             os: ubuntu-22.04
             build_type: Release
             env_cc: gcc-11
             env_cxx: g++-11
             compiler: gcc
-            cppflags: ''
-            ldflags: ''
-            qt_version: 6.9.2
+            qt_version: 6.7.2
             qt_arch_aqt: linux_gcc_64
             qt_arch_dir: gcc_64
             qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport
             qt_tools: ''
+            conan_arch: x86_64
+            conan_compiler: gcc
+            conan_compiler_version: 11
+            conan_compiler_runtime: ''
             conan_package_manager: --conf tools.system.package_manager:mode=install --conf tools.system.package_manager:sudo=True
-            conan_profile: scwx-linux_gcc-11
-            appimage_arch: x86_64
             artifact_suffix: linux-x64
-            compiler_packages: ''
-          - name: linux_clang_x64
-            os: ubuntu-24.04
-            build_type: Release
-            env_cc: clang-17
-            env_cxx: clang++-17
-            compiler: clang
-            cppflags: ''
-            ldflags: ''
-            qt_version: 6.9.2
-            qt_arch_aqt: linux_gcc_64
-            qt_arch_dir: gcc_64
-            qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport
-            qt_tools: ''
-            conan_package_manager: --conf tools.system.package_manager:mode=install --conf tools.system.package_manager:sudo=True
-            conan_profile: scwx-linux_clang-17
-            appimage_arch: x86_64
-            artifact_suffix: linux-clang-x64
-            compiler_packages: clang-17
-          - name: linux_gcc_arm64
-            os: ubuntu-24.04-arm
-            build_type: Release
-            env_cc: gcc-11
-            env_cxx: g++-11
-            compiler: gcc
-            cppflags: ''
-            ldflags: ''
-            qt_version: 6.9.2
-            qt_arch_aqt: linux_gcc_arm64
-            qt_arch_dir: gcc_arm64
-            qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport
-            qt_tools: ''
-            conan_package_manager: --conf tools.system.package_manager:mode=install --conf tools.system.package_manager:sudo=True
-            conan_profile: scwx-linux_gcc-11_armv8
-            appimage_arch: aarch64
-            artifact_suffix: linux-arm64
-            compiler_packages: g++-11
-          - name: macos_clang18_x64
-            os: macos-13
-            build_type: Release
-            env_cc: clang
-            env_cxx: clang++
-            compiler: clang
-            qt_version: 6.9.2
-            qt_arch_aqt: clang_64
-            qt_arch_dir: macos
-            qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport
-            qt_tools: ''
-            conan_package_manager: ''
-            conan_profile: scwx-macos_clang-18
-            appimage_arch: ''
-            artifact_suffix: macos-x64
-          - name: macos_clang18_arm64
-            os: macos-14
-            build_type: Release
-            env_cc: clang
-            env_cxx: clang++
-            compiler: clang
-            qt_version: 6.9.2
-            qt_arch_aqt: clang_64
-            qt_arch_dir: macos
-            qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport
-            qt_tools: ''
-            conan_package_manager: ''
-            conan_profile: scwx-macos_clang-18_armv8
-            appimage_arch: ''
-            artifact_suffix: macos-arm64
     name: ${{ matrix.name }}
     env:
       CC: ${{ matrix.env_cc }}
       CXX: ${{ matrix.env_cxx }}
-      SCWX_VERSION: v0.5.1
+      SCWX_VERSION: v0.4.5
     runs-on: ${{ matrix.os }}
 
     steps:
     - name: Setup
       run: git config --global core.longpaths true
-
+      
     - name: Checkout
-      uses: actions/checkout@v5
+      uses: actions/checkout@v4
       with:
         path: source
         submodules: recursive
 
     - name: Install Qt
-      uses: jdpurcell/install-qt-action@v5
-      env:
-        AQT_CONFIG: ${{ github.workspace }}/source/tools/aqt-settings.ini
+      uses: jurplel/install-qt-action@v3
       with:
         version: ${{ matrix.qt_version }}
         arch: ${{ matrix.qt_arch_aqt }}
@@ -158,79 +89,50 @@ jobs:
         vsversion: ${{ matrix.msvc_version }}
 
     - name: Setup Ubuntu Environment
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
+      if: matrix.os == 'ubuntu-22.04'
       shell: bash
       run: |
         sudo apt-get install doxygen \
                              libfuse2 \
-                             ninja-build \
-                             wayland-protocols \
-                             libwayland-dev \
-                             libwayland-egl-backend-dev \
-                             flatpak \
-                             flatpak-builder \
-                             ${{ matrix.compiler_packages }}
-
-    - name: Setup macOS Environment
-      if: ${{ startsWith(matrix.os, 'macos') }}
-      shell: bash
-      run: |
-        brew install llvm@18
-        LLVM_PATH=$(brew --prefix llvm@18)
-        echo "CC=${LLVM_PATH}/bin/clang" >> $GITHUB_ENV
-        echo "CXX=${LLVM_PATH}/bin/clang++" >> $GITHUB_ENV
-        echo "CPPFLAGS=-I${LLVM_PATH}/include" >> $GITHUB_ENV
-        echo "LDFLAGS=-L${LLVM_PATH}/lib -L${LLVM_PATH}/lib/c++" >> $GITHUB_ENV
+                             ninja-build
 
     - name: Setup Python Environment
       shell: pwsh
       run: |
         pip install geopandas `
-                    GitPython `
-                    conan
-
-    - name: Cache Conan Packages
-      uses: actions/cache@v4
-      with:
-        path: ~/.conan2
-        key: build-${{ matrix.conan_profile }}-${{ hashFiles('./source/conanfile.py', './source/tools/conan/profiles/*') }}
+                    GitPython
 
     - name: Install Conan Packages
       shell: pwsh
       run: |
-        conan config install `
-          ./source/tools/conan/profiles/${{ matrix.conan_profile }} `
-          -tf profiles
-        mkdir build
-        cd build
-        mkdir conan
-        conan install ../source/ `
+        pip install "conan<2.0"
+        conan profile new default --detect
+        conan install ./source/ `
           --remote conancenter `
           --build missing `
-          --profile:all ${{ matrix.conan_profile }} `
-          --settings:all build_type=${{ matrix.build_type }} `
-          --output-folder ./conan/ `
+          --settings arch=${{ matrix.conan_arch }} `
+          --settings build_type=${{ matrix.build_type }} `
+          --settings compiler="${{ matrix.conan_compiler }}" `
+          --settings compiler.version=${{ matrix.conan_compiler_version }} `
+          ${{ matrix.conan_compiler_runtime }} `
           ${{ matrix.conan_package_manager }}
 
     - name: Build Supercell Wx
       shell: pwsh
       run: |
+        mkdir build
         cd build
         cmake ../source/ `
           -G Ninja `
           -DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" `
-          -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES="${{ github.workspace }}/source/external/cmake-conan/conan_provider.cmake" `
-          -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/supercell-wx" `
-          -DCONAN_HOST_PROFILE="${{ matrix.conan_profile }}" `
-          -DCONAN_BUILD_PROFILE="${{ matrix.conan_profile }}"
+          -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/supercell-wx"
         ninja supercell-wx wxtest
 
     - name: Separate Debug Symbols (Linux)
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
+      if: matrix.os == 'ubuntu-22.04'
       shell: bash
       run: |
         cd build/
-        cd Release/
         cd bin/
         objcopy --only-keep-debug supercell-wx supercell-wx.debug
         objcopy --strip-debug --strip-unneeded supercell-wx
@@ -246,7 +148,7 @@ jobs:
         cmake --install . --component supercell-wx
 
     - name: Collect Artifacts
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
+      if: matrix.os == 'ubuntu-22.04'
       shell: bash
       run: |
         pushd supercell-wx/
@@ -259,8 +161,6 @@ jobs:
         cd plugins/
         mkdir -p sqldrivers/
         cp "${RUNNER_WORKSPACE}/Qt/${{ matrix.qt_version }}/${{ matrix.qt_arch_dir }}/plugins/sqldrivers/libqsqlite.so" sqldrivers/
-        mkdir -p platforms/
-        cp ${RUNNER_WORKSPACE}/Qt/${{ matrix.qt_version }}/${{ matrix.qt_arch_dir }}/plugins/platforms/libqwayland* platforms/
         cd ..
         popd
         tar -czf supercell-wx-${{ matrix.artifact_suffix }}.tar.gz supercell-wx/
@@ -277,23 +177,23 @@ jobs:
       uses: actions/upload-artifact@v4
       with:
         name: supercell-wx-debug-${{ matrix.artifact_suffix }}
-        path: ${{ github.workspace }}/build/Release/bin/*.pdb
+        path: ${{ github.workspace }}/build/bin/*.pdb
 
     - name: Upload Artifacts (Linux)
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
+      if: matrix.os == 'ubuntu-22.04'
       uses: actions/upload-artifact@v4
       with:
         name: supercell-wx-${{ matrix.artifact_suffix }}
         path: ${{ github.workspace }}/supercell-wx-${{ matrix.artifact_suffix }}.tar.gz
 
     - name: Upload Debug Artifacts (Linux)
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
+      if: matrix.os == 'ubuntu-22.04'
       uses: actions/upload-artifact@v4
       with:
         name: supercell-wx-debug-${{ matrix.artifact_suffix }}
         path: |
-          ${{ github.workspace }}/build/Release/bin/*.debug
-          ${{ github.workspace }}/build/Release/lib/*.debug
+          ${{ github.workspace }}/build/bin/*.debug
+          ${{ github.workspace }}/build/lib/*.debug
 
     - name: Build Installer (Windows)
       if: matrix.os == 'windows-2022'
@@ -310,90 +210,42 @@ jobs:
         path: ${{ github.workspace }}/build/supercell-wx-*.msi*
 
     - name: Build AppImage (Linux)
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
+      if: matrix.os == 'ubuntu-22.04'
       env:
-        INSTALL_DIR: ${{ github.workspace }}/supercell-wx/
-        APPIMAGE_DIR: ${{ github.workspace }}/supercell-wx-appimage/
-        LDAI_UPDATE_INFORMATION: gh-releases-zsync|dpaulat|supercell-wx|latest|*${{ matrix.appimage_arch }}.AppImage.zsync
-        LDAI_OUTPUT: supercell-wx-${{ env.SCWX_VERSION }}-${{ matrix.appimage_arch }}.AppImage
+        APPIMAGE_DIR: ${{ github.workspace }}/supercell-wx/
+        LDAI_UPDATE_INFORMATION: gh-releases-zsync|dpaulat|supercell-wx|latest|*x86_64.AppImage.zsync
+        LDAI_OUTPUT: supercell-wx-${{ env.SCWX_VERSION }}-x86_64.AppImage
         LINUXDEPLOY_OUTPUT_APP_NAME: supercell-wx
         LINUXDEPLOY_OUTPUT_VERSION: ${{ env.SCWX_VERSION }}
       shell: bash
       run: |
-        wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-${{ matrix.appimage_arch }}.AppImage
-        chmod +x linuxdeploy-${{ matrix.appimage_arch }}.AppImage
+        wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
+        chmod +x linuxdeploy-x86_64.AppImage
         cp "${{ github.workspace }}/source/scwx-qt/res/icons/scwx-256.png" supercell-wx.png
         cp "${{ github.workspace }}/source/scwx-qt/res/linux/supercell-wx.desktop" .
-        cp -r "${{ env.INSTALL_DIR }}" "${{ env.APPIMAGE_DIR }}"
         pushd "${{ env.APPIMAGE_DIR }}"
         mkdir -p usr/
         mv bin/ usr/
         mv lib/ usr/
         mv plugins/ usr/
         popd
-        ./linuxdeploy-${{ matrix.appimage_arch }}.AppImage --appdir ${{ env.APPIMAGE_DIR }} -i supercell-wx.png -d supercell-wx.desktop
-        ./linuxdeploy-${{ matrix.appimage_arch }}.AppImage --appdir ${{ env.APPIMAGE_DIR }} --output appimage
-        rm -f linuxdeploy-${{ matrix.appimage_arch }}.AppImage
+        ./linuxdeploy-x86_64.AppImage --appdir ${{ env.APPIMAGE_DIR }} -i supercell-wx.png -d supercell-wx.desktop
+        ./linuxdeploy-x86_64.AppImage --appdir ${{ env.APPIMAGE_DIR }} --output appimage
+        rm -f linuxdeploy-x86_64.AppImage
 
     - name: Upload AppImage (Linux)
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
+      if: matrix.os == 'ubuntu-22.04'
       uses: actions/upload-artifact@v4
       with:
-        name: supercell-wx-appimage-${{ matrix.artifact_suffix }}
-        path: ${{ github.workspace }}/*-${{ matrix.appimage_arch }}.AppImage*
-
-    - name: Build FlatPak (Linux)
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
-      env:
-        INSTALL_DIR: ${{ github.workspace }}/supercell-wx/
-        FLATPAK_DIR: ${{ github.workspace }}/supercell-wx-flatpak/
-      shell: bash
-      run: |
-        cp -r ${{ env.INSTALL_DIR }} ${{ env.FLATPAK_DIR }}
-        # Copy krb5 libraries to flatpak
-        cp /usr/lib/*/libkrb5.so* \
-           /usr/lib/*/libkrb5support.so* \
-           /usr/lib/*/libgssapi_krb5.so* \
-           /usr/lib/*/libk5crypto.so* \
-           /usr/lib/*/libkeyutils.so* \
-           ${{ env.FLATPAK_DIR }}/lib
-
-        flatpak remote-add --if-not-exists --user flathub https://dl.flathub.org/repo/flathub.flatpakrepo
-        flatpak-builder --force-clean \
-            --user \
-            --install-deps-from=flathub \
-            --repo=flatpak-repo \
-            --install flatpak-build \
-            ${{ github.workspace }}/source/tools/net.supercellwx.app.yml
-        flatpak build-bundle flatpak-repo supercell-wx.flatpak net.supercellwx.app
-
-    - name: Upload FlatPak (Linux)
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
-      uses: actions/upload-artifact@v4
-      with:
-        name: supercell-wx-flatpak-${{ matrix.artifact_suffix }}
-        path: ${{ github.workspace }}/supercell-wx.flatpak
-
-    - name: Build Disk Image (macOS)
-      if: ${{ startsWith(matrix.os, 'macos') }}
-      shell: pwsh
-      run: |
-        cd build
-        cpack
-
-    - name: Upload Disk Image (macOS)
-      if: ${{ startsWith(matrix.os, 'macos') }}
-      uses: actions/upload-artifact@v4
-      with:
-        name: supercell-wx-${{ matrix.artifact_suffix }}
-        path: ${{ github.workspace }}/build/supercell-wx-*.dmg*
+        name: supercell-wx-appimage-x64
+        path: ${{ github.workspace }}/*-x86_64.AppImage*
 
     - name: Test Supercell Wx
       working-directory: ${{ github.workspace }}/build
       env:
         MAPBOX_API_KEY:   ${{ secrets.MAPBOX_API_KEY }}
         MAPTILER_API_KEY: ${{ secrets.MAPTILER_API_KEY }}
-      run: ctest -C ${{ matrix.build_type }} --exclude-regex "test_mln.*|NtpClient.*|UpdateManager.*"
+      run: ctest -C ${{ matrix.build_type }} --exclude-regex test_mln.*
 
     - name: Upload Test Logs
       if: ${{ !cancelled() }}
diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml
deleted file mode 100644
index 7eaa1e95..00000000
--- a/.github/workflows/clang-format-check.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: clang-format-check
-
-on:
-  workflow_dispatch:
-  pull_request:
-    branches:
-      - 'develop'
-
-concurrency:
-  # Cancel in-progress jobs for the same pull request
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  format:
-    runs-on: ubuntu-24.04
-    steps:
-
-    - name: Checkout
-      uses: actions/checkout@v5
-      with:
-        fetch-depth: 0
-        submodules: false
-
-    - name: Update References
-      shell: bash
-      run: |
-        git fetch origin develop
-
-    - name: Setup Ubuntu Environment
-      shell: bash
-      run: |
-        sudo apt-get install clang-format-19
-        sudo rm -f /usr/bin/clang-format
-        sudo ln -s /usr/bin/clang-format-19 /usr/bin/clang-format
-
-    - name: Check Formatting
-      shell: bash
-      run: |
-        MERGE_BASE=$(git merge-base origin/develop ${{ github.event.pull_request.head.sha || github.ref }})
-        echo "Comparing against ${MERGE_BASE}"
-        git clang-format-19 --diff --style=file -v ${MERGE_BASE}
diff --git a/.github/workflows/clang-tidy-comments.yml b/.github/workflows/clang-tidy-comments.yml
deleted file mode 100644
index 9a6df52d..00000000
--- a/.github/workflows/clang-tidy-comments.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-name: Post clang-tidy Review Comments
-
-on:
-  workflow_run:
-    workflows: ["clang-tidy-review"]
-    types:
-      - completed
-
-jobs:
-  build:
-    runs-on: ubuntu-latest
-    if: ${{ github.event.workflow_run.conclusion != 'cancelled' }}
-
-    steps:
-      - name: Post Comments
-        uses: ZedThree/clang-tidy-review/post@v0.21.0
-        with:
-          lgtm_comment_body: ''
-          annotations: false
-          max_comments: 25
-          num_comments_as_exitcode: false
diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml
deleted file mode 100644
index b3de8edf..00000000
--- a/.github/workflows/clang-tidy-review.yml
+++ /dev/null
@@ -1,152 +0,0 @@
-name: clang-tidy-review
-
-on:
-  pull_request:
-    branches:
-      - 'develop'
-
-concurrency:
-  # Cancel in-progress jobs for the same pull request
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  build:
-    strategy:
-      matrix:
-        include:
-          - name: linux_clang-tidy_x64
-            os: ubuntu-24.04
-            build_type: Release
-            env_cc: clang-18
-            env_cxx: clang++-18
-            qt_version: 6.9.2
-            qt_arch_aqt: linux_gcc_64
-            qt_modules: qtimageformats qtmultimedia qtpositioning qtserialport
-            qt_tools: ''
-            conan_package_manager: --conf tools.system.package_manager:mode=install --conf tools.system.package_manager:sudo=True
-            conan_profile: scwx-linux_clang-18
-            compiler_packages: clang-18 clang-tidy-18
-            clang_tidy_binary: clang-tidy-18
-    name: ${{ matrix.name }}
-    runs-on: ${{ matrix.os }}
-    env:
-      CC: ${{ matrix.env_cc }}
-      CXX: ${{ matrix.env_cxx }}
-    steps:
-
-    - name: Checkout
-      uses: actions/checkout@v5
-      with:
-        path: source
-        submodules: recursive
-
-    - name: Checkout clang-tidy-review Repository
-      uses: actions/checkout@v5
-      with:
-        repository: ZedThree/clang-tidy-review
-        ref: v0.20.1
-        path: clang-tidy-review
-
-    - name: Install Qt
-      uses: jdpurcell/install-qt-action@v5
-      env:
-        AQT_CONFIG: ${{ github.workspace }}/source/tools/aqt-settings.ini
-      with:
-        version: ${{ matrix.qt_version }}
-        arch: ${{ matrix.qt_arch_aqt }}
-        modules: ${{ matrix.qt_modules }}
-        tools: ${{ matrix.qt_tools }}
-
-    - name: Setup Ubuntu Environment
-      if: ${{ startsWith(matrix.os, 'ubuntu') }}
-      shell: bash
-      run: |
-        sudo apt-get install doxygen \
-                             libfuse2 \
-                             ninja-build \
-                             wayland-protocols \
-                             libwayland-dev \
-                             libwayland-egl-backend-dev \
-                             ${{ matrix.compiler_packages }}
-
-    - name: Setup Python Environment
-      shell: pwsh
-      run: |
-        pip install geopandas `
-                    GitPython `
-                    conan
-        pip install --break-system-packages clang-tidy-review/post/clang_tidy_review
-
-    - name: Cache Conan Packages
-      uses: actions/cache@v4
-      with:
-        path: ~/.conan2
-        key: build-${{ matrix.conan_profile }}-${{ hashFiles('./source/conanfile.py', './source/tools/conan/profiles/*') }}
-
-    - name: Install Conan Packages
-      shell: pwsh
-      run: |
-        conan config install `
-          ./source/tools/conan/profiles/${{ matrix.conan_profile }} `
-          -tf profiles
-        mkdir build
-        cd build
-        mkdir conan
-        conan install ../source/ `
-          --remote conancenter `
-          --build missing `
-          --profile:all ${{ matrix.conan_profile }} `
-          --settings:all build_type=${{ matrix.build_type }} `
-          --output-folder ./conan/ `
-          ${{ matrix.conan_package_manager }}
-
-    - name: Autogenerate
-      shell: pwsh
-      run: |
-        cd build
-        cmake ../source/ `
-          -G Ninja `
-          -DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" `
-          -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES="${{ github.workspace }}/source/external/cmake-conan/conan_provider.cmake" `
-          -DCMAKE_INSTALL_PREFIX="${{ github.workspace }}/supercell-wx" `
-          -DCONAN_HOST_PROFILE="${{ matrix.conan_profile }}" `
-          -DCONAN_BUILD_PROFILE="${{ matrix.conan_profile }}" `
-          -DCMAKE_EXPORT_COMPILE_COMMANDS=on
-        ninja glad_gl_core_33 `
-              scwx-qt_generate_counties_db `
-              scwx-qt_generate_versions `
-              scwx-qt_autogen
-
-    - name: Code Review
-      id: review
-      shell: bash
-      run: |
-        cd source
-        review --clang_tidy_binary=${{ matrix.clang_tidy_binary }} \
-               --token=${{ github.token }} \
-               --repo='${{ github.repository }}' \
-               --pr='${{ github.event.pull_request.number }}' \
-               --build_dir='../build' \
-               --base_dir='${{ github.workspace }}/source' \
-               --clang_tidy_checks='' \
-               --config_file='' \
-               --include='*.[ch],*.[ch]xx,*.[chi]pp,*.[ch]++,*.cc,*.hh' \
-               --exclude='' \
-               --apt-packages='' \
-               --cmake-command='' \
-               --max-comments=25 \
-               --lgtm-comment-body='' \
-               --split_workflow=true \
-               --annotations=false \
-               --parallel=0
-        rsync -avzh --ignore-missing-args clang-tidy-review-output.json ../
-        rsync -avzh --ignore-missing-args clang-tidy-review-metadata.json ../
-        rsync -avzh --ignore-missing-args clang_fixes.json ../
-
-    - name: Upload Review
-      uses: ZedThree/clang-tidy-review/upload@v0.21.0
-
-    - name: Status Check
-      if: steps.review.outputs.total_comments > 0
-      run: exit 1
diff --git a/.gitignore b/.gitignore
index cb46cec7..9e6da487 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,20 +5,9 @@ CMakeLists.txt.user
 CMakeCache.txt
 CMakeFiles
 CMakeScripts
-CMakeUserPresets.json
 Testing
 cmake_install.cmake
 install_manifest.txt
 compile_commands.json
 CTestTestfile.cmake
 _deps
-
-# Editor directories
-.idea/
-.vs/
-
-# Python Virtual Environment
-.venv/
-
-# Specific excludes for Supercell Wx
-tools/lib/user-setup.sh
diff --git a/.gitmodules b/.gitmodules
index 5bf3b307..52ede30b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,45 +1,39 @@
-[submodule "data"]
-	path = data
-	url = https://github.com/dpaulat/supercell-wx-data
-[submodule "external/aws-sdk-cpp"]
-	path = external/aws-sdk-cpp
-	url = https://github.com/aws/aws-sdk-cpp.git
 [submodule "external/cmake-conan"]
 	path = external/cmake-conan
 	url = https://github.com/conan-io/cmake-conan.git
-[submodule "external/date"]
-	path = external/date
-	url = https://github.com/HowardHinnant/date.git
-[submodule "external/glad"]
-	path = external/glad
-	url = https://github.com/Dav1dde/glad.git
+[submodule "test/data"]
+	path = test/data
+	url = https://github.com/dpaulat/supercell-wx-test-data
 [submodule "external/hsluv-c"]
 	path = external/hsluv-c
 	url = https://github.com/hsluv/hsluv-c.git
+[submodule "external/stb"]
+	path = external/stb
+	url = https://github.com/nothings/stb.git
+[submodule "data"]
+	path = data
+	url = https://github.com/dpaulat/supercell-wx-data
 [submodule "external/imgui"]
 	path = external/imgui
 	url = https://github.com/ocornut/imgui.git
 [submodule "external/imgui-backend-qt"]
 	path = external/imgui-backend-qt
 	url = https://github.com/dpaulat/imgui-backend-qt
-[submodule "external/maplibre-native"]
-	path = external/maplibre-native
-	url = https://github.com/dpaulat/maplibre-gl-native.git
-[submodule "external/maplibre-native-qt"]
-	path = external/maplibre-native-qt
-	url = https://github.com/dpaulat/maplibre-native-qt.git
-[submodule "external/qt6ct"]
-	path = external/qt6ct
-	url = https://github.com/AdenKoperczak/qt6ct.git
-[submodule "external/stb"]
-	path = external/stb
-	url = https://github.com/nothings/stb.git
-[submodule "external/textflowcpp"]
-	path = external/textflowcpp
-	url = https://github.com/catchorg/textflowcpp.git
+[submodule "external/aws-sdk-cpp"]
+	path = external/aws-sdk-cpp
+	url = https://github.com/aws/aws-sdk-cpp.git
+[submodule "external/date"]
+	path = external/date
+	url = https://github.com/HowardHinnant/date.git
 [submodule "external/units"]
 	path = external/units
 	url = https://github.com/nholthaus/units.git
-[submodule "test/data"]
-	path = test/data
-	url = https://github.com/dpaulat/supercell-wx-test-data
+[submodule "external/textflowcpp"]
+	path = external/textflowcpp
+	url = https://github.com/catchorg/textflowcpp.git
+[submodule "external/maplibre-native-qt"]
+	path = external/maplibre-native-qt
+	url = https://github.com/dpaulat/maplibre-native-qt.git
+[submodule "external/maplibre-native"]
+	path = external/maplibre-native
+	url = https://github.com/dpaulat/maplibre-gl-native.git
diff --git a/ACKNOWLEDGEMENTS.md b/ACKNOWLEDGEMENTS.md
index 1608b2ae..154d0f6c 100644
--- a/ACKNOWLEDGEMENTS.md
+++ b/ACKNOWLEDGEMENTS.md
@@ -23,7 +23,7 @@ Supercell Wx uses code from the following dependencies:
 | [FreeType GL](https://github.com/rougier/freetype-gl) | [BSD 2-Clause with views sentence](https://spdx.org/licenses/BSD-2-Clause-Views.html) |
 | [GeographicLib](https://geographiclib.sourceforge.io/) | [MIT License](https://spdx.org/licenses/MIT.html) |
 | [geos](https://libgeos.org/) | [GNU Lesser General Public License v2.1 or later](https://spdx.org/licenses/LGPL-2.1-or-later.html) |
-| [GLAD](https://github.com/Dav1dde/glad) | [MIT License](https://spdx.org/licenses/MIT.html) |
+| [GLEW](https://www.opengl.org/sdk/libs/GLEW/) | [MIT License](https://spdx.org/licenses/MIT.html) |
 | [GLM](https://github.com/g-truc/glm) | [MIT License](https://spdx.org/licenses/MIT.html) |
 | [GoogleTest](https://google.github.io/googletest/) | [BSD 3-Clause "New" or "Revised" License](https://spdx.org/licenses/BSD-3-Clause.html) |
 | [HSLuv](https://www.hsluv.org/) | [MIT License](https://spdx.org/licenses/MIT.html) |
@@ -32,12 +32,9 @@ Supercell Wx uses code from the following dependencies:
 | [libpng](http://libpng.org/pub/png/libpng.html) | [PNG Reference Library version 2](https://spdx.org/licenses/libpng-2.0.html) |
 | [libxml2](http://xmlsoft.org/) | [MIT License](https://spdx.org/licenses/MIT.html) |
 | [MapLibre Native](https://maplibre.org/projects/maplibre-native/) | [BSD 2-Clause "Simplified" License](https://spdx.org/licenses/BSD-2-Clause.html) |
-| [Mesa 3D](https://mesa3d.org/) | [MIT License](https://spdx.org/licenses/MIT.html) |
 | [nunicode](https://bitbucket.org/alekseyt/nunicode/src/master/) | [MIT License](https://spdx.org/licenses/MIT.html) | Modified for MapLibre Native |
 | [OpenSSL](https://www.openssl.org/) | [OpenSSL License](https://spdx.org/licenses/OpenSSL.html) |
 | [Qt](https://www.qt.io/) | [GNU Lesser General Public License v3.0 only](https://spdx.org/licenses/LGPL-3.0-only.html) | Qt Core, Qt GUI, Qt Multimedia, Qt Network, Qt OpenGL, Qt Positioning, Qt Serial Port, Qt SQL, Qt SVG, Qt Widgets
Additional Licenses: https://doc.qt.io/qt-6/licenses-used-in-qt.html |
-| [qt6ct](https://github.com/trialuser02/qt6ct) | [BSD 2-Clause "Simplified" License](https://spdx.org/licenses/BSD-2-Clause.html) |
-| [range-v3](https://github.com/ericniebler/range-v3) | [Boost Software License 1.0](https://spdx.org/licenses/BSL-1.0.html)
[MIT License](https://spdx.org/licenses/MIT.html)
[Stepanov and McJones, "Elements of Programming" license](https://github.com/ericniebler/range-v3/tree/0.12.0?tab=License-1-ov-file)
[SGI C++ Standard Template Library license](https://github.com/ericniebler/range-v3/tree/0.12.0?tab=License-1-ov-file) |
 | [re2](https://github.com/google/re2) | [BSD 3-Clause "New" or "Revised" License](https://spdx.org/licenses/BSD-3-Clause.html) |
 | [spdlog](https://github.com/gabime/spdlog) | [MIT License](https://spdx.org/licenses/MIT.html) |
 | [SQLite](https://www.sqlite.org/) | Public Domain |
@@ -70,7 +67,6 @@ Supercell Wx uses assets from the following sources:
 | [Font Awesome Free](https://fontawesome.com/) | CC BY 4.0 License |
 | [Inconsolata](https://fonts.google.com/specimen/Inconsolata) | SIL Open Font License |
 | [NOAA's Weather and Climate Toolkit](https://www.ncdc.noaa.gov/wct/) | Public Domain | Default Color Tables |
-| [qt6ct](https://github.com/trialuser02/qt6ct) | [BSD 2-Clause "Simplified" License](https://spdx.org/licenses/BSD-2-Clause.html) |
 | [Roboto Flex](https://fonts.google.com/specimen/Roboto+Flex) | SIL Open Font License |
 | [Supercell thunderstorm with dramatic clouds](https://www.shutterstock.com/image-photo/supercell-thunderstorm-dramatic-clouds-1354353521) | Shutterstock Standard License | Photo by John Sirlin
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e76cb8d1..c06a46e6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,14 +1,7 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.21)
 set(PROJECT_NAME supercell-wx)
-
-include(tools/scwx_config.cmake)
-
-set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0)
-
-scwx_python_setup()
-
 project(${PROJECT_NAME}
-        VERSION      0.5.1
+        VERSION      0.4.5
         DESCRIPTION  "Supercell Wx is a free, open source advanced weather radar viewer."
         HOMEPAGE_URL "https://github.com/dpaulat/supercell-wx"
         LANGUAGES    C CXX)
@@ -18,21 +11,40 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
 set(CMAKE_POLICY_DEFAULT_CMP0079 NEW)
 set(CMAKE_POLICY_DEFAULT_CMP0148 OLD) # aws-sdk-cpp uses FindPythonInterp
 
-scwx_output_dirs_setup()
-
 enable_testing()
 
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 
+include(${PROJECT_SOURCE_DIR}/external/cmake-conan/conan.cmake)
+
 set_property(DIRECTORY
              APPEND
              PROPERTY CMAKE_CONFIGURE_DEPENDS
              conanfile.py)
 
+# Don't use RelWithDebInfo Conan packages
+if (${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
+    set(conan_build_type "Release")
+else()
+    set(conan_build_type ${CMAKE_BUILD_TYPE})
+endif()
+
+conan_cmake_autodetect(settings
+                       BUILD_TYPE ${conan_build_type})
+
+conan_cmake_install(PATH_OR_REFERENCE ${PROJECT_SOURCE_DIR}
+                    BUILD missing
+                    REMOTE conancenter
+                    SETTINGS ${settings})
+
+include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
+include(${CMAKE_BINARY_DIR}/conan_paths.cmake)
+conan_basic_setup(TARGETS)
+
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_NO_LIB")
 
 set(SCWX_DIR ${PROJECT_SOURCE_DIR})
-set(SCWX_VERSION "0.5.1")
+set(SCWX_VERSION "0.4.5")
 
 option(SCWX_ADDRESS_SANITIZER "Build with Address Sanitizer" OFF)
 
diff --git a/CMakePresets.json b/CMakePresets.json
deleted file mode 100644
index ec997016..00000000
--- a/CMakePresets.json
+++ /dev/null
@@ -1,383 +0,0 @@
-{
-  "version": 5,
-  "cmakeMinimumRequired": {
-    "major": 3,
-    "minor": 24,
-    "patch": 0
-  },
-  "configurePresets": [
-    {
-      "name": "base",
-      "hidden": true,
-      "generator": "Ninja",
-      "binaryDir": "${sourceDir}/build/${presetName}",
-      "cacheVariables": {
-        "CMAKE_PROJECT_TOP_LEVEL_INCLUDES": "${sourceDir}/external/cmake-conan/conan_provider.cmake",
-        "SCWX_VIRTUAL_ENV": "${sourceDir}/.venv"
-      }
-    },
-    {
-      "name": "windows-base",
-      "inherits": "base",
-      "hidden": true,
-      "generator": "Visual Studio 17 2022",
-      "condition": {
-        "type": "equals",
-        "lhs": "${hostSystemName}",
-        "rhs": "Windows"
-      },
-      "vendor": {
-        "microsoft.com/VisualStudioSettings/CMake/1.0": {
-          "hostOS": [
-            "Windows"
-          ]
-        }
-      }
-    },
-    {
-      "name": "windows-x64-base",
-      "inherits": "windows-base",
-      "hidden": true
-    },
-    {
-      "name": "linux-base",
-      "inherits": "base",
-      "hidden": true,
-      "condition": {
-        "type": "equals",
-        "lhs": "${hostSystemName}",
-        "rhs": "Linux"
-      }
-    },
-    {
-      "name": "windows-msvc2022-x64-base",
-      "inherits": "windows-x64-base",
-      "hidden": true,
-      "cacheVariables": {
-        "CMAKE_PREFIX_PATH": "C:/Qt/6.9.2/msvc2022_64"
-      }
-    },
-    {
-      "name": "windows-msvc2022-x64-ninja-base",
-      "inherits": "windows-msvc2022-x64-base",
-      "hidden": true,
-      "generator": "Ninja",
-      "cacheVariables": {
-        "CMAKE_PREFIX_PATH": "C:/Qt/6.9.2/msvc2022_64"
-      }
-    },
-    {
-      "name": "linux-gcc-base",
-      "inherits": "linux-base",
-      "hidden": true,
-      "cacheVariables": {
-        "CMAKE_PREFIX_PATH": "/opt/Qt/6.9.2/gcc_64"
-      },
-      "environment": {
-        "CC": "gcc-11",
-        "CXX": "g++-11"
-      }
-    },
-    {
-      "name": "windows-msvc2022-x64-debug",
-      "inherits": "windows-msvc2022-x64-base",
-      "displayName": "Windows MSVC 2022 x64 Debug",
-      "architecture": {
-        "value": "x64",
-        "strategy": "external"
-      },
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Debug",
-        "CONAN_HOST_PROFILE": "scwx-windows_msvc2022_x64-debug",
-        "CONAN_BUILD_PROFILE": "scwx-windows_msvc2022_x64-debug"
-      }
-    },
-    {
-      "name": "windows-msvc2022-x64-release",
-      "inherits": "windows-msvc2022-x64-base",
-      "displayName": "Windows MSVC 2022 x64 Release",
-      "architecture": {
-        "value": "x64",
-        "strategy": "external"
-      },
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CONAN_HOST_PROFILE": "scwx-windows_msvc2022_x64",
-        "CONAN_BUILD_PROFILE": "scwx-windows_msvc2022_x64"
-      }
-    },
-    {
-      "name": "windows-msvc2022-x64-ninja-debug",
-      "inherits": "windows-msvc2022-x64-ninja-base",
-      "displayName": "Windows MSVC 2022 x64 Ninja Debug",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Debug",
-        "CONAN_HOST_PROFILE": "scwx-windows_msvc2022_x64-debug",
-        "CONAN_BUILD_PROFILE": "scwx-windows_msvc2022_x64-debug"
-      }
-    },
-    {
-      "name": "windows-msvc2022-x64-ninja-release",
-      "inherits": "windows-msvc2022-x64-ninja-base",
-      "displayName": "Windows MSVC 2022 x64 Ninja Release",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CONAN_HOST_PROFILE": "scwx-windows_msvc2022_x64",
-        "CONAN_BUILD_PROFILE": "scwx-windows_msvc2022_x64"
-      }
-    },
-    {
-      "name": "linux-gcc-debug",
-      "inherits": "linux-gcc-base",
-      "displayName": "Linux GCC Debug",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Debug",
-        "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/${presetName}/Debug/supercell-wx",
-        "CONAN_HOST_PROFILE": "scwx-linux_gcc-11-debug",
-        "CONAN_BUILD_PROFILE": "scwx-linux_gcc-11-debug"
-      }
-    },
-    {
-      "name": "linux-gcc-release",
-      "inherits": "linux-gcc-base",
-      "displayName": "Linux GCC Release",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/${presetName}/Release/supercell-wx",
-        "CONAN_HOST_PROFILE": "scwx-linux_gcc-11",
-        "CONAN_BUILD_PROFILE": "scwx-linux_gcc-11"
-      }
-    },
-    {
-      "name": "linux-gcc-debug-asan",
-      "inherits": "linux-gcc-base",
-      "displayName": "Linux GCC Debug Address Sanitizer",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Debug",
-        "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/${presetName}/Debug/supercell-wx",
-        "CONAN_HOST_PROFILE": "scwx-linux_gcc-11-debug",
-        "CONAN_BUILD_PROFILE": "scwx-linux_gcc-11-debug",
-        "SCWX_ADDRESS_SANITIZER": {
-          "type": "BOOL",
-          "value": "ON"
-        }
-      }
-    },
-    {
-      "name": "linux-gcc-release-asan",
-      "inherits": "linux-gcc-base",
-      "displayName": "Linux GCC Release Address Sanitizer",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CMAKE_INSTALL_PREFIX": "${sourceDir}/build/${presetName}/Release/supercell-wx",
-        "CONAN_HOST_PROFILE": "scwx-linux_gcc-11",
-        "CONAN_BUILD_PROFILE": "scwx-linux_gcc-11",
-        "SCWX_ADDRESS_SANITIZER": {
-          "type": "BOOL",
-          "value": "ON"
-        }
-      }
-    },
-    {
-      "name": "ci-linux-gcc14",
-      "inherits": "linux-gcc-base",
-      "displayName": "CI Linux GCC 14",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CONAN_HOST_PROFILE": "scwx-linux_gcc-14",
-        "CONAN_BUILD_PROFILE": "scwx-linux_gcc-14"
-      },
-      "environment": {
-        "CC": "gcc-14",
-        "CXX": "g++-14"
-      }
-    },
-    {
-      "name": "ci-linux-clang17",
-      "inherits": "linux-gcc-base",
-      "displayName": "CI Linux Clang 17",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CONAN_HOST_PROFILE": "scwx-linux_clang-17",
-        "CONAN_BUILD_PROFILE": "scwx-linux_clang-17"
-      },
-      "environment": {
-        "CC": "clang-17",
-        "CXX": "clang++-17"
-      }
-    },
-    {
-      "name": "ci-linux-gcc-arm64",
-      "inherits": "linux-gcc-base",
-      "displayName": "CI Linux GCC ARM64",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CMAKE_PREFIX_PATH": "/opt/Qt/6.9.2/gcc_arm64",
-        "CONAN_HOST_PROFILE": "scwx-linux_gcc-11_armv8",
-        "CONAN_BUILD_PROFILE": "scwx-linux_gcc-11_armv8"
-      },
-      "environment": {
-        "CC": "gcc-11",
-        "CXX": "g++-11"
-      }
-    },
-    {
-      "name": "macos-base",
-      "inherits": "base",
-      "hidden": true,
-      "cacheVariables": {
-        "CMAKE_PREFIX_PATH": "$env{HOME}/Qt/6.9.2/macos"
-      },
-      "condition": {
-        "type": "equals",
-        "lhs": "${hostSystemName}",
-        "rhs": "Darwin"
-      }
-    },
-    {
-      "name": "macos-clang18-base",
-      "inherits": "macos-base",
-      "hidden": true
-    },
-    {
-      "name": "macos-clang18-x64-base",
-      "inherits": "macos-clang18-base",
-      "hidden": true,
-      "environment": {
-        "CC": "/usr/local/opt/llvm@18/bin/clang",
-        "CXX": "/usr/local/opt/llvm@18/bin/clang++",
-        "PATH": "/usr/local/opt/llvm@18/bin:$penv{PATH}",
-        "CPPFLAGS": "-I/usr/local/opt/llvm@18/include",
-        "LDFLAGS": "-L/usr/local/opt/llvm@18/lib -L/usr/local/opt/llvm@18/lib/c++"
-      }
-    },
-    {
-      "name": "macos-clang18-arm64-base",
-      "inherits": "macos-clang18-base",
-      "hidden": true,
-      "environment": {
-        "CC": "/opt/homebrew/opt/llvm@18/bin/clang",
-        "CXX": "/opt/homebrew/opt/llvm@18/bin/clang++",
-        "PATH": "/opt/homebrew/opt/llvm@18/bin:$penv{PATH}",
-        "CPPFLAGS": "-I/opt/homebrew/opt/llvm@18/include",
-        "LDFLAGS": "-L/opt/homebrew/opt/llvm@18/lib -L/opt/homebrew/opt/llvm@18/lib/c++"
-      }
-    },
-    {
-      "name": "macos-clang18-x64-debug",
-      "inherits": "macos-clang18-x64-base",
-      "displayName": "macOS Clang 18 x64 Debug",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Debug",
-        "CONAN_HOST_PROFILE": "scwx-macos_clang-18-debug",
-        "CONAN_BUILD_PROFILE": "scwx-macos_clang-18-debug"
-      }
-    },
-    {
-      "name": "macos-clang18-x64-release",
-      "inherits": "macos-clang18-x64-base",
-      "displayName": "macOS Clang 18 x64 Release",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CONAN_HOST_PROFILE": "scwx-macos_clang-18",
-        "CONAN_BUILD_PROFILE": "scwx-macos_clang-18"
-      }
-    },
-    {
-      "name": "macos-clang18-arm64-debug",
-      "inherits": "macos-clang18-arm64-base",
-      "displayName": "macOS Clang 18 Arm64 Debug",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Debug",
-        "CONAN_HOST_PROFILE": "scwx-macos_clang-18_armv8-debug",
-        "CONAN_BUILD_PROFILE": "scwx-macos_clang-18_armv8-debug"
-      }
-    },
-    {
-      "name": "macos-clang18-arm64-release",
-      "inherits": "macos-clang18-arm64-base",
-      "displayName": "macOS Clang 18 Arm64 Release",
-      "cacheVariables": {
-        "CMAKE_BUILD_TYPE": "Release",
-        "CONAN_HOST_PROFILE": "scwx-macos_clang-18_armv8",
-        "CONAN_BUILD_PROFILE": "scwx-macos_clang-18_armv8"
-      }
-    }
-  ],
-  "buildPresets": [
-    {
-      "name": "windows-msvc2022-x64-debug",
-      "configurePreset": "windows-msvc2022-x64-debug",
-      "displayName": "Windows MSVC 2022 x64 Debug",
-      "configuration": "Debug"
-    },
-    {
-      "name": "windows-msvc2022-x64-release",
-      "configurePreset": "windows-msvc2022-x64-release",
-      "displayName": "Windows MSVC 2022 x64 Release",
-      "configuration": "Release"
-    },
-    {
-      "name": "linux-gcc-debug",
-      "configurePreset": "linux-gcc-debug",
-      "displayName": "Linux GCC Debug",
-      "configuration": "Debug"
-    },
-    {
-      "name": "linux-gcc-release",
-      "configurePreset": "linux-gcc-release",
-      "displayName": "Linux GCC Release",
-      "configuration": "Release"
-    },
-    {
-      "name": "macos-clang18-x64-debug",
-      "configurePreset": "macos-clang18-x64-debug",
-      "displayName": "macOS Clang 18 x64 Debug",
-      "configuration": "Debug"
-    },
-    {
-      "name": "macos-clang18-x64-release",
-      "configurePreset": "macos-clang18-x64-release",
-      "displayName": "macOS Clang 18 x64 Release",
-      "configuration": "Release"
-    },
-    {
-      "name": "macos-clang18-arm64-debug",
-      "configurePreset": "macos-clang18-arm64-debug",
-      "displayName": "macOS Clang 18 Arm64 Debug",
-      "configuration": "Debug"
-    },
-    {
-      "name": "macos-clang18-arm64-release",
-      "configurePreset": "macos-clang18-arm64-release",
-      "displayName": "macOS Clang 18 Arm64 Release",
-      "configuration": "Release"
-    }
-  ],
-  "testPresets": [
-    {
-      "name": "windows-msvc2022-x64-debug",
-      "configurePreset": "windows-msvc2022-x64-debug",
-      "displayName": "Windows MSVC 2022 x64 Debug",
-      "configuration": "Debug"
-    },
-    {
-      "name": "windows-msvc2022-x64-release",
-      "configurePreset": "windows-msvc2022-x64-release",
-      "displayName": "Windows MSVC 2022 x64 Release",
-      "configuration": "Release"
-    },
-    {
-      "name": "linux-gcc-debug",
-      "configurePreset": "linux-gcc-debug",
-      "displayName": "Linux GCC Debug",
-      "configuration": "Debug"
-    },
-    {
-      "name": "linux-gcc-release",
-      "configurePreset": "linux-gcc-release",
-      "displayName": "Linux GCC Release",
-      "configuration": "Release"
-    }
-  ]
-}
diff --git a/LICENSE.txt b/LICENSE.txt
index 799086a0..8c9c5fbd 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright (c) 2021-2025 Dan Paulat
+Copyright (c) 2021-2024 Dan Paulat
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 81b0e698..321c33f2 100644
--- a/README.md
+++ b/README.md
@@ -26,36 +26,32 @@ Supercell Wx supports the following 64-bit operating systems:
   - Fedora Linux 34+
   - openSUSE Tumbleweed
   - Ubuntu 22.04+
-  - NixOS 25.05+
   - Most distributions supporting the GCC Standard C++ Library 11+
-- macOS
-  - 13.6+ for Intel-based Macs
-  - 14.0+ for Apple silicon-based Macs
-
+ 
 ## Linux Dependencies
 
 Supercell Wx requires the following Linux dependencies:
 
-- Linux/X11 (Wayland works too) with support for GCC 11, OpenGL 3.3 and OpenGL ES 3.0
+- Linux/X11 (Wayland works too) with support for GCC 11 and OpenGL 3.3
 - X11/XCB libraries including xcb-cursor
-
+ 
 ## FAQ
 
 Frequently asked questions:
 
 - Q: Why is the map black when loading for the first time?
-
+  
   - A. You must obtain a free API key from either (or both) [MapTiler](https://cloud.maptiler.com/auth/widget?next=https://cloud.maptiler.com/maps/) which currently does not require a credit/debit card, or [Mapbox](https://account.mapbox.com/) which ***does*** require a credit/debit card, but as of writing, you will receive 200K free requests per month, which should be sufficient for an individual user.
 
 - Q: Why is it that when I change my color table, API key, grid width/height settings, nothing happens after hitting apply?
 
   - A. As of right now, you must restart Supercell Wx in order to apply these changes. In future iterations, this will no longer be an issue.
-
+   
 - Q. Is it possible to get dark mode?
-
+ 
   - A. In Windows, make sure to set the flag `-style fusion` at the end of the target path of the .exe
     - Example: `C:\Users\Administrator\Desktop\Supercell-Wx\bin\supercell-wx.exe -style fusion`
   - A. In Linux, if you're using KDE, Supercell Wx should automatically follow your theme settings.
-
+    
 - Q: How can I contribute?
   - A. Head to [Developer Setup](https://supercell-wx.readthedocs.io/en/stable/development/developer-setup.html) and [Contributing](CONTRIBUTING.md) to configure the Supercell Wx development environment for your IDE. Currently Visual Studio and Visual Studio Code are recommended, with other IDEs remaining untested at this time.
diff --git a/conanfile.py b/conanfile.py
index 6803a56c..b18f2d31 100644
--- a/conanfile.py
+++ b/conanfile.py
@@ -1,66 +1,37 @@
-from conan import ConanFile
-from conan.tools.cmake import CMake
-from conan.tools.files import copy
-import os
+from conans import ConanFile
 
 class SupercellWxConan(ConanFile):
     settings   = ("os", "compiler", "build_type", "arch")
-    requires   = ("boost/1.88.0",
-                  "cpr/1.12.0",
+    requires   = ("boost/1.85.0",
+                  "cpr/1.10.5",
                   "fontconfig/2.15.0",
                   "freetype/2.13.2",
-                  "geographiclib/2.4",
-                  "geos/3.13.0",
-                  "glm/1.0.1",
-                  "gtest/1.17.0",
-                  "libcurl/8.12.1",
-                  "libpng/1.6.50",
-                  "libxml2/2.14.5",
-                  "openssl/3.5.0",
-                  "range-v3/0.12.0",
-                  "re2/20250722",
-                  "spdlog/1.15.1",
-                  "sqlite3/3.49.1",
-                  "vulkan-loader/1.3.290.0",
+                  "geographiclib/2.3",
+                  "geos/3.12.2",
+                  "glew/2.2.0",
+                  "glm/cci.20230113",
+                  "gtest/1.15.0",
+                  "libcurl/8.9.1",
+                  "libxml2/2.12.7",
+                  "openssl/3.3.1",
+                  "re2/20240702",
+                  "spdlog/1.14.1",
+                  "sqlite3/3.46.0",
+                  "vulkan-loader/1.3.243.0",
                   "zlib/1.3.1")
-    generators = ("CMakeDeps")
-    default_options = {"geos/*:shared"     : True,
-                       "libiconv/*:shared" : True}
-
-    def configure(self):
-        if self.settings.os == "Windows":
-            self.options["libcurl"].with_ssl = "schannel"
-        elif self.settings.os == "Linux":
-            self.options["openssl"].shared    = True
-            self.options["libcurl"].ca_bundle = "none"
-            self.options["libcurl"].ca_path   = "none"
-        elif self.settings.os == "Macos":
-            self.options["openssl"].shared    = True
-            self.options["libcurl"].ca_bundle = "none"
-            self.options["libcurl"].ca_path   = "none"
+    generators = ("cmake",
+                  "cmake_find_package",
+                  "cmake_paths")
+    default_options = {"geos:shared"      : True,
+                       "libiconv:shared"  : True,
+                       "openssl:no_module": True,
+                       "openssl:shared"   : True}
 
     def requirements(self):
         if self.settings.os == "Linux":
-            self.requires("mesa-glu/9.0.3")
-            self.requires("onetbb/2022.2.0")
+            self.requires("onetbb/2021.12.0")
 
-    def generate(self):
-        build_folder = os.path.join(self.build_folder,
-                                    "..",
-                                    str(self.settings.build_type),
-                                    self.cpp_info.bindirs[0])
-
-        for dep in self.dependencies.values():
-            if dep.cpp_info.bindirs:
-                copy(self, "*.dll", dep.cpp_info.bindirs[0], build_folder)
-            if dep.cpp_info.libdirs:
-                copy(self, "*.dylib", dep.cpp_info.libdirs[0], build_folder)
-
-    def build(self):
-        cmake = CMake(self)
-        cmake.configure()
-        cmake.build()
-
-    def package(self):
-        cmake = CMake(self)
-        cmake.install()
+    def imports(self):
+        self.copy("*.dll", dst="bin", src="bin")
+        self.copy("*.dylib", dst="bin", src="lib")
+        self.copy("license*", dst="licenses", src=".", folder=True, ignore_case=True)
diff --git a/data b/data
index fd72b32c..8eb89b19 160000
--- a/data
+++ b/data
@@ -1 +1 @@
-Subproject commit fd72b32cc12419b4a9c9a72487e58ffa04fb2a70
+Subproject commit 8eb89b19fdd1c78e896cc6cb47e07425bb473699
diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt
index 1039e96e..bbc76c64 100644
--- a/external/CMakeLists.txt
+++ b/external/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-external)
 
 set_property(DIRECTORY
@@ -6,22 +6,18 @@ set_property(DIRECTORY
              PROPERTY CMAKE_CONFIGURE_DEPENDS
              aws-sdk-cpp.cmake
              date.cmake
-             glad.cmake
              hsluv-c.cmake
              imgui.cmake
              maplibre-native-qt.cmake
              stb.cmake
              textflowcpp.cmake
-             units.cmake
-             qt6ct.cmake)
+             units.cmake)
 
 include(aws-sdk-cpp.cmake)
 include(date.cmake)
-include(glad.cmake)
 include(hsluv-c.cmake)
 include(imgui.cmake)
 include(maplibre-native-qt.cmake)
 include(stb.cmake)
 include(textflowcpp.cmake)
 include(units.cmake)
-include(qt6ct.cmake)
diff --git a/external/aws-sdk-cpp b/external/aws-sdk-cpp
index 8d31e042..d5eb42fe 160000
--- a/external/aws-sdk-cpp
+++ b/external/aws-sdk-cpp
@@ -1 +1 @@
-Subproject commit 8d31e042f950fe70924391a205cceaf342ecec00
+Subproject commit d5eb42fe7c632868d4535b454ee2e5ba0e349b7f
diff --git a/external/aws-sdk-cpp.cmake b/external/aws-sdk-cpp.cmake
index f88df9ca..1ba641db 100644
--- a/external/aws-sdk-cpp.cmake
+++ b/external/aws-sdk-cpp.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-aws-sdk-cpp)
 
 set(AWS_SDK_WARNINGS_ARE_ERRORS OFF)
@@ -21,10 +21,6 @@ set(MINIMIZE_SIZE      OFF  CACHE BOOL   "If enabled, the SDK will be built via
 # Save off ${CMAKE_CXX_FLAGS} before modifying compiler settings
 set(CMAKE_CXX_FLAGS_PREV "${CMAKE_CXX_FLAGS}")
 
-# Configure OpenSSL crypto library
-find_package(OpenSSL)
-add_library(crypto ALIAS OpenSSL::Crypto)
-
 # Fix CMake errors for internal variables not set
 include(aws-sdk-cpp/cmake/compiler_settings.cmake)
 set_msvc_warnings()
diff --git a/external/cmake-conan b/external/cmake-conan
index b0e4d1ec..b240c809 160000
--- a/external/cmake-conan
+++ b/external/cmake-conan
@@ -1 +1 @@
-Subproject commit b0e4d1ec08edb35ef31033938567d621f6643c17
+Subproject commit b240c809b5ea097077fc8222cad71d2329288e48
diff --git a/external/date b/external/date
index a5db3aec..cc4685a2 160000
--- a/external/date
+++ b/external/date
@@ -1 +1 @@
-Subproject commit a5db3aecec580bc78b6c01c118f2628676769b69
+Subproject commit cc4685a21e4a4fdae707ad1233c61bbaff241f93
diff --git a/external/date.cmake b/external/date.cmake
index 7fce7ffc..a804f68c 100644
--- a/external/date.cmake
+++ b/external/date.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-date)
 
 set(USE_SYSTEM_TZ_DB ON)
diff --git a/external/glad b/external/glad
deleted file mode 160000
index 73db193f..00000000
--- a/external/glad
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 73db193f853e2ee079bf3ca8a64aa2eaf6459043
diff --git a/external/glad.cmake b/external/glad.cmake
deleted file mode 100644
index 59ceb179..00000000
--- a/external/glad.cmake
+++ /dev/null
@@ -1,11 +0,0 @@
-cmake_minimum_required(VERSION 3.24)
-set(PROJECT_NAME scwx-glad)
-
-# Path to glad directory
-set(GLAD_SOURCES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/glad/")
-
-# Path to glad CMake files
-add_subdirectory("${GLAD_SOURCES_DIR}/cmake" glad_cmake)
-
-# Specify glad settings
-glad_add_library(glad_gl_core_33 LOADER REPRODUCIBLE API gl:core=3.3)
diff --git a/external/hsluv-c b/external/hsluv-c
index 982217c6..59539e04 160000
--- a/external/hsluv-c
+++ b/external/hsluv-c
@@ -1 +1 @@
-Subproject commit 982217c65a9ff574302335177d2dc078d9bfa6f5
+Subproject commit 59539e04a6fa648935cbe57c2104041f23136c4a
diff --git a/external/hsluv-c.cmake b/external/hsluv-c.cmake
index eec8f0f2..0129d39d 100644
--- a/external/hsluv-c.cmake
+++ b/external/hsluv-c.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-hsluv-c)
 
 set(HSLUV_C_TESTS OFF)
diff --git a/external/imgui b/external/imgui
index 45acd5e0..6ccc561a 160000
--- a/external/imgui
+++ b/external/imgui
@@ -1 +1 @@
-Subproject commit 45acd5e0e82f4c954432533ae9985ff0e1aad6d5
+Subproject commit 6ccc561a2ab497ad4ae6ee1dbd3b992ffada35cb
diff --git a/external/imgui-backend-qt b/external/imgui-backend-qt
index 023345ca..0fe974eb 160000
--- a/external/imgui-backend-qt
+++ b/external/imgui-backend-qt
@@ -1 +1 @@
-Subproject commit 023345ca8abf731fc50568c0197ceebe76bb4324
+Subproject commit 0fe974ebd037844c9f23d6325dbcc128e9973749
diff --git a/external/imgui.cmake b/external/imgui.cmake
index 3eba6ee0..443817ef 100644
--- a/external/imgui.cmake
+++ b/external/imgui.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-imgui)
 
 find_package(QT NAMES Qt6
@@ -12,7 +12,7 @@ find_package(Qt${QT_VERSION_MAJOR}
              
 find_package(Freetype)
 
-set(IMGUI_SOURCES include/scwx/external/imgui/imconfig.h
+set(IMGUI_SOURCES imgui/imconfig.h
                   imgui/imgui.cpp
                   imgui/imgui.h
                   imgui/imgui_demo.cpp
@@ -33,9 +33,8 @@ set(IMGUI_SOURCES include/scwx/external/imgui/imconfig.h
 add_library(imgui STATIC ${IMGUI_SOURCES})
 
 target_include_directories(imgui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/imgui)
-target_include_directories(imgui PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
 
-target_compile_definitions(imgui PUBLIC IMGUI_USER_CONFIG=)
+target_compile_definitions(imgui PRIVATE IMGUI_ENABLE_FREETYPE)
 
 target_link_libraries(imgui PRIVATE Qt${QT_VERSION_MAJOR}::Widgets
                                     Freetype::Freetype)
diff --git a/external/include/scwx/external/imgui/imconfig.h b/external/include/scwx/external/imgui/imconfig.h
deleted file mode 100644
index ac954a77..00000000
--- a/external/include/scwx/external/imgui/imconfig.h
+++ /dev/null
@@ -1,147 +0,0 @@
-// clang-format off
-//-----------------------------------------------------------------------------
-// DEAR IMGUI COMPILE-TIME OPTIONS
-// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
-// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
-//-----------------------------------------------------------------------------
-// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
-// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
-//-----------------------------------------------------------------------------
-// You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp
-// files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
-// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
-// Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
-//-----------------------------------------------------------------------------
-
-#pragma once
-
-//---- Define assertion handler. Defaults to calling assert().
-// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
-//#define IM_ASSERT(_EXPR)  MyAssert(_EXPR)
-//#define IM_ASSERT(_EXPR)  ((void)(_EXPR))     // Disable asserts
-
-//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
-// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
-// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
-//   for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
-//#define IMGUI_API __declspec(dllexport)                   // MSVC Windows: DLL export
-//#define IMGUI_API __declspec(dllimport)                   // MSVC Windows: DLL import
-//#define IMGUI_API __attribute__((visibility("default")))  // GCC/Clang: override visibility when set is hidden
-
-//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
-#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
-
-//---- Disable all of Dear ImGui or don't implement standard windows/tools.
-// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
-//#define IMGUI_DISABLE                                     // Disable everything: all headers and source files will be empty.
-//#define IMGUI_DISABLE_DEMO_WINDOWS                        // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
-//#define IMGUI_DISABLE_DEBUG_TOOLS                         // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
-
-//---- Don't implement some functions to reduce linkage requirements.
-//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS   // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a)
-//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS          // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
-//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS         // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
-//#define IMGUI_DISABLE_WIN32_FUNCTIONS                     // [Win32] Won't use and link with any Win32 function (clipboard, IME).
-//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS      // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
-//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS             // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
-//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS            // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
-//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS              // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
-//#define IMGUI_DISABLE_FILE_FUNCTIONS                      // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
-//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS              // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
-//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS                  // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
-//#define IMGUI_DISABLE_DEFAULT_FONT                        // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
-//#define IMGUI_DISABLE_SSE                                 // Disable use of SSE intrinsics even if available
-
-//---- Enable Test Engine / Automation features.
-//#define IMGUI_ENABLE_TEST_ENGINE                          // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
-
-//---- Include imgui_user.h at the end of imgui.h as a convenience
-// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
-//#define IMGUI_INCLUDE_IMGUI_USER_H
-//#define IMGUI_USER_H_FILENAME         "my_folder/my_imgui_user.h"
-
-//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
-//#define IMGUI_USE_BGRA_PACKED_COLOR
-
-//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
-//#define IMGUI_USE_LEGACY_CRC32_ADLER
-
-//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
-//#define IMGUI_USE_WCHAR32
-
-//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
-// By default the embedded implementations are declared static and not available outside of Dear ImGui sources files.
-//#define IMGUI_STB_TRUETYPE_FILENAME   "my_folder/stb_truetype.h"
-//#define IMGUI_STB_RECT_PACK_FILENAME  "my_folder/stb_rect_pack.h"
-//#define IMGUI_STB_SPRINTF_FILENAME    "my_folder/stb_sprintf.h"    // only used if IMGUI_USE_STB_SPRINTF is defined.
-//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
-//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
-//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION                   // only disabled if IMGUI_USE_STB_SPRINTF is defined.
-
-//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
-// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
-//#define IMGUI_USE_STB_SPRINTF
-
-//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
-// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
-// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
-#define IMGUI_ENABLE_FREETYPE
-
-//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
-// Only works in combination with IMGUI_ENABLE_FREETYPE.
-// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
-// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
-// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
-//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
-//#define IMGUI_ENABLE_FREETYPE_LUNASVG
-
-//---- Use stb_truetype to build and rasterize the font atlas (default)
-// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
-//#define IMGUI_ENABLE_STB_TRUETYPE
-
-//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
-// This will be inlined as part of ImVec2 and ImVec4 class declarations.
-/*
-#define IM_VEC2_CLASS_EXTRA                                                     \
-        constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {}                   \
-        operator MyVec2() const { return MyVec2(x,y); }
-
-#define IM_VEC4_CLASS_EXTRA                                                     \
-        constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {}   \
-        operator MyVec4() const { return MyVec4(x,y,z,w); }
-*/
-//---- ...Or use Dear ImGui's own very basic math operators.
-//#define IMGUI_DEFINE_MATH_OPERATORS
-
-//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
-// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
-// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
-// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
-//#define ImDrawIdx unsigned int
-
-//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
-//struct ImDrawList;
-//struct ImDrawCmd;
-//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
-//#define ImDrawCallback MyImDrawCallback
-
-//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
-// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
-//#define IM_DEBUG_BREAK  IM_ASSERT(0)
-//#define IM_DEBUG_BREAK  __debugbreak()
-
-//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
-// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
-//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
-
-//---- Debug Tools: Enable slower asserts
-//#define IMGUI_DEBUG_PARANOID
-
-//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
-/*
-namespace ImGui
-{
-    void MyFunction(const char* name, MyMatrix44* mtx);
-}
-*/
-// clang-format on
diff --git a/external/maplibre-native b/external/maplibre-native
index 3654f5fa..3d4ca3fd 160000
--- a/external/maplibre-native
+++ b/external/maplibre-native
@@ -1 +1 @@
-Subproject commit 3654f5fa9f06534d7fd2d95b810049a82e5953ef
+Subproject commit 3d4ca3fdf07c50db3002b11bff93c81ec380e493
diff --git a/external/maplibre-native-qt b/external/maplibre-native-qt
index 8b406978..805ccf62 160000
--- a/external/maplibre-native-qt
+++ b/external/maplibre-native-qt
@@ -1 +1 @@
-Subproject commit 8b40697895c19da4479cd037a76608f4c36935e8
+Subproject commit 805ccf6204a546e43fed599631ad5d698f68ae86
diff --git a/external/maplibre-native-qt.cmake b/external/maplibre-native-qt.cmake
index 7db42c10..6a508040 100644
--- a/external/maplibre-native-qt.cmake
+++ b/external/maplibre-native-qt.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-mln)
 
 set(gtest_disable_pthreads ON)
@@ -19,28 +19,11 @@ if (MSVC)
     target_link_options(MLNQtCore PRIVATE "$<$:/DEBUG>")
     target_link_options(MLNQtCore PRIVATE "$<$:/OPT:REF>")
     target_link_options(MLNQtCore PRIVATE "$<$:/OPT:ICF>")
-
-    # Enable multi-processor compilation
-    target_compile_options(MLNQtCore PRIVATE "/MP")
-    target_compile_options(mbgl-core PRIVATE "/MP")
-    target_compile_options(mbgl-vendor-csscolorparser PRIVATE "/MP")
-    target_compile_options(mbgl-vendor-nunicode PRIVATE "/MP")
-    target_compile_options(mbgl-vendor-parsedate PRIVATE "/MP")
-
-    if (TARGET mbgl-vendor-sqlite)
-        target_compile_options(mbgl-vendor-sqlite PRIVATE "/MP")
-    endif()
 else()
     target_compile_options(mbgl-core PRIVATE "$<$:-g>")
     target_compile_options(MLNQtCore PRIVATE "$<$:-g>")
 endif()
 
-if (APPLE)
-    # Enable GL check error debug
-    target_compile_definitions(mbgl-core PRIVATE MLN_GL_CHECK_ERRORS=1)
-    target_compile_definitions(MLNQtCore PRIVATE MLN_GL_CHECK_ERRORS=1)
-endif()
-
 set(MLN_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/maplibre-native/include
                      ${CMAKE_CURRENT_SOURCE_DIR}/maplibre-native-qt/src/core/include
                      ${CMAKE_CURRENT_BINARY_DIR}/maplibre-native-qt/src/core/include PARENT_SCOPE)
diff --git a/external/qt6ct b/external/qt6ct
deleted file mode 160000
index 2c569c6c..00000000
--- a/external/qt6ct
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2c569c6c4776ea5a1299030c079b16f70473c9e6
diff --git a/external/qt6ct.cmake b/external/qt6ct.cmake
deleted file mode 100644
index cdae0b25..00000000
--- a/external/qt6ct.cmake
+++ /dev/null
@@ -1,62 +0,0 @@
-cmake_minimum_required(VERSION 3.16.0)
-set(PROJECT_NAME scwx-qt6ct)
-
-find_package(QT NAMES Qt6
-             COMPONENTS Gui Widgets
-             REQUIRED)
-find_package(Qt${QT_VERSION_MAJOR}
-             COMPONENTS Gui Widgets
-             REQUIRED)
-
-#extract version from qt6ct.h
-file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/qt6ct/src/qt6ct-common/qt6ct.h"
-     QT6CT_VERSION_DATA REGEX "^#define[ \t]+QT6CT_VERSION_[A-Z]+[ \t]+[0-9]+.*$")
-
-if(QT6CT_VERSION_DATA)
-  foreach(item IN ITEMS MAJOR MINOR)
-    string(REGEX REPLACE ".*#define[ \t]+QT6CT_VERSION_${item}[ \t]+([0-9]+).*"
-       "\\1" QT6CT_VERSION_${item} ${QT6CT_VERSION_DATA})
-  endforeach()
-  set(QT6CT_VERSION "${QT6CT_VERSION_MAJOR}.${QT6CT_VERSION_MINOR}")
-  set(QT6CT_SOVERSION "${QT6CT_VERSION_MAJOR}")
-  message(STATUS "qt6ct version: ${QT6CT_VERSION}")
-else()
-  message(FATAL_ERROR "invalid header")
-endif()
-
-set(qt6ct-common-source
-  qt6ct/src/qt6ct-common/qt6ct.cpp
-)
-
-set(qt6ct-widgets-source
-  qt6ct/src/qt6ct/paletteeditdialog.cpp
-  qt6ct/src/qt6ct/paletteeditdialog.ui
-)
-
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTORCC ON)
-set(CMAKE_AUTOUIC ON)
-
-include_directories(qt6ct/src/qt6ct-common)
-
-add_library(qt6ct-common STATIC ${qt6ct-common-source})
-set_target_properties(qt6ct-common PROPERTIES VERSION ${QT6CT_VERSION})
-target_link_libraries(qt6ct-common PRIVATE Qt6::Gui)
-target_compile_definitions(qt6ct-common PRIVATE QT6CT_LIBRARY)
-
-add_library(qt6ct-widgets STATIC ${qt6ct-widgets-source})
-set_target_properties(qt6ct-widgets PROPERTIES VERSION ${QT6CT_VERSION})
-target_link_libraries(qt6ct-widgets PRIVATE Qt6::Widgets qt6ct-common)
-target_compile_definitions(qt6ct-widgets PRIVATE QT6CT_LIBRARY)
-
-if (MSVC)
-    # Produce PDB file for debug
-    target_compile_options(qt6ct-common PRIVATE "$<$:/Zi>")
-    target_compile_options(qt6ct-widgets PRIVATE "$<$:/Zi>")
-else()
-    target_compile_options(qt6ct-common PRIVATE "$<$:-g>")
-    target_compile_options(qt6ct-widgets PRIVATE "$<$:-g>")
-endif()
-
-target_include_directories( qt6ct-common INTERFACE qt6ct/src )
-target_include_directories( qt6ct-widgets INTERFACE qt6ct/src )
diff --git a/external/stb b/external/stb
index f58f558c..beebb24b 160000
--- a/external/stb
+++ b/external/stb
@@ -1 +1 @@
-Subproject commit f58f558c120e9b32c217290b80bad1a0729fbb2c
+Subproject commit beebb24b945efdea3b9bba23affb8eb3ba8982e7
diff --git a/external/stb.cmake b/external/stb.cmake
index 570af425..c26bedaf 100644
--- a/external/stb.cmake
+++ b/external/stb.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-stb)
 
 set(STB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stb PARENT_SCOPE)
diff --git a/external/textflowcpp.cmake b/external/textflowcpp.cmake
index 31020665..1e36da18 100644
--- a/external/textflowcpp.cmake
+++ b/external/textflowcpp.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-textflowcpp)
 
 set(TEXTFLOWCPP_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/textflowcpp PARENT_SCOPE)
diff --git a/external/units.cmake b/external/units.cmake
index cc70ac1c..d037ae54 100644
--- a/external/units.cmake
+++ b/external/units.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.20)
 set(PROJECT_NAME scwx-units)
 
 add_subdirectory(units)
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index afdd2a37..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-conan
-geopandas
-GitPython
diff --git a/scwx-qt/CMakeLists.txt b/scwx-qt/CMakeLists.txt
index e47bc3fb..f4e636e7 100644
--- a/scwx-qt/CMakeLists.txt
+++ b/scwx-qt/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.21)
 
 set_property(DIRECTORY
              APPEND
diff --git a/scwx-qt/gl/map_color.vert b/scwx-qt/gl/map_color.vert
index 609d1c34..6ae98e92 100644
--- a/scwx-qt/gl/map_color.vert
+++ b/scwx-qt/gl/map_color.vert
@@ -26,9 +26,6 @@ void main()
    // Always set displayed to true
    vsOut.displayed = 1;
 
-   // Initialize texCoord to default value
-   vsOut.texCoord = vec3(0.0f, 0.0f, 0.0f);
-
    // Pass the threshold and time range to the geometry shader
    vsOut.threshold = aThreshold;
    vsOut.timeRange = aTimeRange;
diff --git a/scwx-qt/gl/radar.frag b/scwx-qt/gl/radar.frag
index b6491e8b..0c605b8a 100644
--- a/scwx-qt/gl/radar.frag
+++ b/scwx-qt/gl/radar.frag
@@ -9,14 +9,14 @@ uniform float uDataMomentScale;
 
 uniform bool uCFPEnabled;
 
-in float dataMoment;
-in float cfpMoment;
+flat in uint dataMoment;
+flat in uint cfpMoment;
 
 layout (location = 0) out vec4 fragColor;
 
 void main()
 {
-   float texCoord = (dataMoment - float(uDataMomentOffset)) / uDataMomentScale;
+   float texCoord = float(dataMoment - uDataMomentOffset) / uDataMomentScale;
 
    if (uCFPEnabled && cfpMoment > 8u)
    {
diff --git a/scwx-qt/gl/radar.vert b/scwx-qt/gl/radar.vert
index 97754b73..b4da9f17 100644
--- a/scwx-qt/gl/radar.vert
+++ b/scwx-qt/gl/radar.vert
@@ -13,8 +13,8 @@ layout (location = 2) in uint aCfpMoment;
 uniform mat4 uMVPMatrix;
 uniform vec2 uMapScreenCoord;
 
-out float dataMoment;
-out float cfpMoment;
+flat out uint dataMoment;
+flat out uint cfpMoment;
 
 vec2 latLngToScreenCoordinate(in vec2 latLng)
 {
diff --git a/scwx-qt/gl/threshold.geom b/scwx-qt/gl/threshold.geom
index deead87d..677a80cd 100644
--- a/scwx-qt/gl/threshold.geom
+++ b/scwx-qt/gl/threshold.geom
@@ -21,9 +21,7 @@ smooth out vec4 color;
 void main()
 {
    if (gsIn[0].displayed != 0 &&
-       (gsIn[0].threshold == 0 ||            // If Threshold: 0 was specified, no threshold
-        uMapDistance == 0 ||                 // If uMapDistance is zero, threshold is disabled
-        (gsIn[0].threshold < 0 && -(gsIn[0].threshold) <= uMapDistance) || // If Threshold is negative and below current map distance
+       (gsIn[0].threshold <= 0 ||            // If Threshold: 0 was specified, no threshold
         gsIn[0].threshold >= uMapDistance || // If Threshold is above current map distance
         gsIn[0].threshold >= 999) &&         // If Threshold: 999 was specified (or greater), no threshold
        (gsIn[0].timeRange[0] == 0 ||              // If there is no start time specified
diff --git a/scwx-qt/res/config/radar_sites.json b/scwx-qt/res/config/radar_sites.json
index 20d7397c..8f2a1b90 100644
--- a/scwx-qt/res/config/radar_sites.json
+++ b/scwx-qt/res/config/radar_sites.json
@@ -67,7 +67,7 @@
 	{ "type": "wsr88d", "id": "KLVX", "lat": 37.9753058, "lon": -85.9438455,  "country": "USA", "state": "KY",  "place": "Louisville",              "tz": "America/New_York",             "elevation": 833.0 },
 	{ "type": "wsr88d", "id": "KPAH", "lat": 37.068333,  "lon": -88.771944,   "country": "USA", "state": "KY",  "place": "Paducah",                 "tz": "America/Chicago",              "elevation": 506.0 },
 	{ "type": "wsr88d", "id": "KPOE", "lat": 31.1556923, "lon": -92.9762596,  "country": "USA", "state": "LA",  "place": "Fort Polk",               "tz": "America/Chicago",              "elevation": 473.0 },
-	{ "type": "wsr88d", "id": "KHDC", "lat": 30.5196,    "lon": -90.4074,     "country": "USA", "state": "LA",  "place": "New Orleans (Hammond)",   "tz": "America/Chicago",              "elevation": 43.0 },
+	{ "type": "wsr88d", "id": "KHDC", "lat": 30.519306,  "lon": -90.424028,   "country": "USA", "state": "LA",  "place": "New Orleans (Hammond)",   "tz": "America/Chicago",              "elevation": 43.0 },
 	{ "type": "wsr88d", "id": "KLCH", "lat": 30.125306,  "lon": -93.215889,   "country": "USA", "state": "LA",  "place": "Lake Charles",            "tz": "America/Chicago",              "elevation": 137.0 },
 	{ "type": "wsr88d", "id": "KSHV", "lat": 32.450833,  "lon": -93.84125,    "country": "USA", "state": "LA",  "place": "Shreveport",              "tz": "America/Chicago",              "elevation": 387.0 },
 	{ "type": "wsr88d", "id": "KLIX", "lat": 30.3367133, "lon": -89.8256618,  "country": "USA", "state": "LA",  "place": "New Orleans (Slidell)",   "tz": "America/Chicago",              "elevation": 179.0 },
diff --git a/scwx-qt/res/icons/font-awesome-6/briefcase-solid.svg b/scwx-qt/res/icons/font-awesome-6/briefcase-solid.svg
deleted file mode 100644
index b16bc330..00000000
--- a/scwx-qt/res/icons/font-awesome-6/briefcase-solid.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/building-columns-solid.svg b/scwx-qt/res/icons/font-awesome-6/building-columns-solid.svg
deleted file mode 100644
index cf0df19a..00000000
--- a/scwx-qt/res/icons/font-awesome-6/building-columns-solid.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/building-solid.svg b/scwx-qt/res/icons/font-awesome-6/building-solid.svg
deleted file mode 100644
index 6f6d3f24..00000000
--- a/scwx-qt/res/icons/font-awesome-6/building-solid.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/caravan-solid.svg b/scwx-qt/res/icons/font-awesome-6/caravan-solid.svg
deleted file mode 100644
index c341214f..00000000
--- a/scwx-qt/res/icons/font-awesome-6/caravan-solid.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/house-solid-white.svg b/scwx-qt/res/icons/font-awesome-6/house-solid-white.svg
deleted file mode 100644
index 59f65e1e..00000000
--- a/scwx-qt/res/icons/font-awesome-6/house-solid-white.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/location-crosshairs-solid.svg b/scwx-qt/res/icons/font-awesome-6/location-crosshairs-solid.svg
deleted file mode 100644
index 5bb1ea5c..00000000
--- a/scwx-qt/res/icons/font-awesome-6/location-crosshairs-solid.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/location-pin.svg b/scwx-qt/res/icons/font-awesome-6/location-pin.svg
deleted file mode 100644
index 4b6182cd..00000000
--- a/scwx-qt/res/icons/font-awesome-6/location-pin.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/star-solid-white.svg b/scwx-qt/res/icons/font-awesome-6/star-solid-white.svg
deleted file mode 100644
index 41bcd103..00000000
--- a/scwx-qt/res/icons/font-awesome-6/star-solid-white.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/font-awesome-6/tent-solid.svg b/scwx-qt/res/icons/font-awesome-6/tent-solid.svg
deleted file mode 100644
index 9f159d60..00000000
--- a/scwx-qt/res/icons/font-awesome-6/tent-solid.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/scwx-qt/res/icons/scwx.icns b/scwx-qt/res/icons/scwx.icns
deleted file mode 100644
index bda4ea32..00000000
Binary files a/scwx-qt/res/icons/scwx.icns and /dev/null differ
diff --git a/scwx-qt/res/linux/net.supercellwx.app.desktop b/scwx-qt/res/linux/net.supercellwx.app.desktop
deleted file mode 100644
index 8e671522..00000000
--- a/scwx-qt/res/linux/net.supercellwx.app.desktop
+++ /dev/null
@@ -1,7 +0,0 @@
-[Desktop Entry]
-Type=Application
-Name=Supercell Wx
-Comment=Weather Radar and Data Viewer
-Exec=supercell-wx
-Icon=net.supercellwx.app.png
-Categories=Network;Science;
diff --git a/scwx-qt/res/scwx-qt.plist.in b/scwx-qt/res/scwx-qt.plist.in
deleted file mode 100644
index f9db57c0..00000000
--- a/scwx-qt/res/scwx-qt.plist.in
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-	CFBundleInfoDictionaryVersion
-	6.0
-	CFBundlePackageType
-	APPL
-
-	CFBundleName
-	${MACOSX_BUNDLE_BUNDLE_NAME}
-	CFBundleIdentifier
-	${MACOSX_BUNDLE_GUI_IDENTIFIER}
-	CFBundleExecutable
-	${MACOSX_BUNDLE_EXECUTABLE_NAME}
-
-	CFBundleVersion
-	${MACOSX_BUNDLE_BUNDLE_VERSION}
-	CFBundleShortVersionString
-	${MACOSX_BUNDLE_SHORT_VERSION_STRING}
-
-	LSMinimumSystemVersion
-	${CMAKE_OSX_DEPLOYMENT_TARGET}
-
-	NSHumanReadableCopyright
-	${MACOSX_BUNDLE_COPYRIGHT}
-
-	CFBundleIconFile
-	${MACOSX_BUNDLE_ICON_FILE}
-
-	CFBundleDevelopmentRegion
-	en
-	CFBundleAllowMixedLocalizations
-	
-
-	NSPrincipalClass
-	NSApplication
-
-	NSSupportsAutomaticGraphicsSwitching
-	
-
-
diff --git a/scwx-qt/res/textures/images/dot.svg b/scwx-qt/res/textures/images/dot.svg
deleted file mode 100644
index 8f765035..00000000
--- a/scwx-qt/res/textures/images/dot.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-
diff --git a/scwx-qt/res/textures/images/location-marker.svg b/scwx-qt/res/textures/images/location-marker.svg
deleted file mode 100644
index 3eef9d9e..00000000
--- a/scwx-qt/res/textures/images/location-marker.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
diff --git a/scwx-qt/scwx-qt.cmake b/scwx-qt/scwx-qt.cmake
index ecd26ef9..78d45217 100644
--- a/scwx-qt/scwx-qt.cmake
+++ b/scwx-qt/scwx-qt.cmake
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.24)
+cmake_minimum_required(VERSION 3.21)
 
 project(scwx-qt LANGUAGES CXX)
 
@@ -6,19 +6,17 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
 
 set(CMAKE_AUTOUIC ON)
 set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTORCC OFF)
+set(CMAKE_AUTORCC ON)
 
 set(CMAKE_CXX_STANDARD 20)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
-OPTION(SCWX_DISABLE_CONSOLE "Disables the Windows console in release mode" ON)
-
 find_package(Boost)
 find_package(Fontconfig)
 find_package(geographiclib)
 find_package(geos)
+find_package(GLEW)
 find_package(glm)
-find_package(OpenGL)
 find_package(Python COMPONENTS Interpreter)
 find_package(SQLite3)
 
@@ -32,9 +30,7 @@ find_package(QT NAMES Qt6
                         Positioning
                         SerialPort
                         Svg
-                        Widgets
-                        Sql
-             REQUIRED)
+                        Widgets REQUIRED)
 
 find_package(Qt${QT_VERSION_MAJOR}
              COMPONENTS Gui
@@ -53,13 +49,9 @@ find_package(Qt${QT_VERSION_MAJOR}
 set(SRC_EXE_MAIN source/scwx/qt/main/main.cpp)
 
 set(HDR_MAIN source/scwx/qt/main/application.hpp
-             source/scwx/qt/main/check_privilege.hpp
-             source/scwx/qt/main/main_window.hpp
-             source/scwx/qt/main/process_validation.hpp)
+             source/scwx/qt/main/main_window.hpp)
 set(SRC_MAIN source/scwx/qt/main/application.cpp
-             source/scwx/qt/main/check_privilege.cpp
-             source/scwx/qt/main/main_window.cpp
-             source/scwx/qt/main/process_validation.cpp)
+             source/scwx/qt/main/main_window.cpp)
 set(UI_MAIN  source/scwx/qt/main/main_window.ui)
 set(HDR_CONFIG source/scwx/qt/config/county_database.hpp
                source/scwx/qt/config/radar_site.hpp)
@@ -103,13 +95,11 @@ set(HDR_MANAGER source/scwx/qt/manager/alert_manager.hpp
                 source/scwx/qt/manager/log_manager.hpp
                 source/scwx/qt/manager/media_manager.hpp
                 source/scwx/qt/manager/placefile_manager.hpp
-                source/scwx/qt/manager/marker_manager.hpp
                 source/scwx/qt/manager/position_manager.hpp
                 source/scwx/qt/manager/radar_product_manager.hpp
                 source/scwx/qt/manager/radar_product_manager_notifier.hpp
                 source/scwx/qt/manager/resource_manager.hpp
                 source/scwx/qt/manager/settings_manager.hpp
-                source/scwx/qt/manager/task_manager.hpp
                 source/scwx/qt/manager/text_event_manager.hpp
                 source/scwx/qt/manager/thread_manager.hpp
                 source/scwx/qt/manager/timeline_manager.hpp
@@ -121,13 +111,11 @@ set(SRC_MANAGER source/scwx/qt/manager/alert_manager.cpp
                 source/scwx/qt/manager/log_manager.cpp
                 source/scwx/qt/manager/media_manager.cpp
                 source/scwx/qt/manager/placefile_manager.cpp
-                source/scwx/qt/manager/marker_manager.cpp
                 source/scwx/qt/manager/position_manager.cpp
                 source/scwx/qt/manager/radar_product_manager.cpp
                 source/scwx/qt/manager/radar_product_manager_notifier.cpp
                 source/scwx/qt/manager/resource_manager.cpp
                 source/scwx/qt/manager/settings_manager.cpp
-                source/scwx/qt/manager/task_manager.cpp
                 source/scwx/qt/manager/text_event_manager.cpp
                 source/scwx/qt/manager/thread_manager.cpp
                 source/scwx/qt/manager/timeline_manager.cpp
@@ -144,7 +132,6 @@ set(HDR_MAP source/scwx/qt/map/alert_layer.hpp
             source/scwx/qt/map/overlay_layer.hpp
             source/scwx/qt/map/overlay_product_layer.hpp
             source/scwx/qt/map/placefile_layer.hpp
-            source/scwx/qt/map/marker_layer.hpp
             source/scwx/qt/map/radar_product_layer.hpp
             source/scwx/qt/map/radar_range_layer.hpp
             source/scwx/qt/map/radar_site_layer.hpp)
@@ -159,7 +146,6 @@ set(SRC_MAP source/scwx/qt/map/alert_layer.cpp
             source/scwx/qt/map/overlay_layer.cpp
             source/scwx/qt/map/overlay_product_layer.cpp
             source/scwx/qt/map/placefile_layer.cpp
-            source/scwx/qt/map/marker_layer.cpp
             source/scwx/qt/map/radar_product_layer.cpp
             source/scwx/qt/map/radar_range_layer.cpp
             source/scwx/qt/map/radar_site_layer.cpp)
@@ -168,7 +154,6 @@ set(HDR_MODEL source/scwx/qt/model/alert_model.hpp
               source/scwx/qt/model/imgui_context_model.hpp
               source/scwx/qt/model/layer_model.hpp
               source/scwx/qt/model/placefile_model.hpp
-              source/scwx/qt/model/marker_model.hpp
               source/scwx/qt/model/radar_site_model.hpp
               source/scwx/qt/model/tree_item.hpp
               source/scwx/qt/model/tree_model.hpp)
@@ -177,7 +162,6 @@ set(SRC_MODEL source/scwx/qt/model/alert_model.cpp
               source/scwx/qt/model/imgui_context_model.cpp
               source/scwx/qt/model/layer_model.cpp
               source/scwx/qt/model/placefile_model.cpp
-              source/scwx/qt/model/marker_model.cpp
               source/scwx/qt/model/radar_site_model.cpp
               source/scwx/qt/model/tree_item.cpp
               source/scwx/qt/model/tree_model.cpp)
@@ -185,11 +169,9 @@ set(HDR_REQUEST source/scwx/qt/request/download_request.hpp
                 source/scwx/qt/request/nexrad_file_request.hpp)
 set(SRC_REQUEST source/scwx/qt/request/download_request.cpp
                 source/scwx/qt/request/nexrad_file_request.cpp)
-set(HDR_SETTINGS source/scwx/qt/settings/alert_palette_settings.hpp
-                 source/scwx/qt/settings/audio_settings.hpp
+set(HDR_SETTINGS source/scwx/qt/settings/audio_settings.hpp
                  source/scwx/qt/settings/general_settings.hpp
                  source/scwx/qt/settings/hotkey_settings.hpp
-                 source/scwx/qt/settings/line_settings.hpp
                  source/scwx/qt/settings/map_settings.hpp
                  source/scwx/qt/settings/palette_settings.hpp
                  source/scwx/qt/settings/product_settings.hpp
@@ -203,11 +185,9 @@ set(HDR_SETTINGS source/scwx/qt/settings/alert_palette_settings.hpp
                  source/scwx/qt/settings/text_settings.hpp
                  source/scwx/qt/settings/ui_settings.hpp
                  source/scwx/qt/settings/unit_settings.hpp)
-set(SRC_SETTINGS source/scwx/qt/settings/alert_palette_settings.cpp
-                 source/scwx/qt/settings/audio_settings.cpp
+set(SRC_SETTINGS source/scwx/qt/settings/audio_settings.cpp
                  source/scwx/qt/settings/general_settings.cpp
                  source/scwx/qt/settings/hotkey_settings.cpp
-                 source/scwx/qt/settings/line_settings.cpp
                  source/scwx/qt/settings/map_settings.cpp
                  source/scwx/qt/settings/palette_settings.cpp
                  source/scwx/qt/settings/product_settings.cpp
@@ -230,7 +210,6 @@ set(HDR_TYPES source/scwx/qt/types/alert_types.hpp
               source/scwx/qt/types/layer_types.hpp
               source/scwx/qt/types/location_types.hpp
               source/scwx/qt/types/map_types.hpp
-              source/scwx/qt/types/marker_types.hpp
               source/scwx/qt/types/media_types.hpp
               source/scwx/qt/types/qt_types.hpp
               source/scwx/qt/types/radar_product_record.hpp
@@ -259,13 +238,10 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp
            source/scwx/qt/ui/alert_dialog.hpp
            source/scwx/qt/ui/alert_dock_widget.hpp
            source/scwx/qt/ui/animation_dock_widget.hpp
-           source/scwx/qt/ui/api_key_edit_widget.hpp
            source/scwx/qt/ui/collapsible_group.hpp
            source/scwx/qt/ui/county_dialog.hpp
-           source/scwx/qt/ui/custom_layer_dialog.hpp
+           source/scwx/qt/ui/wfo_dialog.hpp
            source/scwx/qt/ui/download_dialog.hpp
-           source/scwx/qt/ui/edit_line_dialog.hpp
-           source/scwx/qt/ui/edit_marker_dialog.hpp
            source/scwx/qt/ui/flow_layout.hpp
            source/scwx/qt/ui/gps_info_dialog.hpp
            source/scwx/qt/ui/hotkey_edit.hpp
@@ -276,29 +252,22 @@ set(HDR_UI source/scwx/qt/ui/about_dialog.hpp
            source/scwx/qt/ui/level2_products_widget.hpp
            source/scwx/qt/ui/level2_settings_widget.hpp
            source/scwx/qt/ui/level3_products_widget.hpp
-           source/scwx/qt/ui/line_label.hpp
            source/scwx/qt/ui/open_url_dialog.hpp
            source/scwx/qt/ui/placefile_dialog.hpp
            source/scwx/qt/ui/placefile_settings_widget.hpp
-           source/scwx/qt/ui/marker_dialog.hpp
-           source/scwx/qt/ui/marker_settings_widget.hpp
            source/scwx/qt/ui/progress_dialog.hpp
            source/scwx/qt/ui/radar_site_dialog.hpp
            source/scwx/qt/ui/serial_port_dialog.hpp
            source/scwx/qt/ui/settings_dialog.hpp
-           source/scwx/qt/ui/update_dialog.hpp
-           source/scwx/qt/ui/wfo_dialog.hpp)
+           source/scwx/qt/ui/update_dialog.hpp)
 set(SRC_UI source/scwx/qt/ui/about_dialog.cpp
            source/scwx/qt/ui/alert_dialog.cpp
            source/scwx/qt/ui/alert_dock_widget.cpp
            source/scwx/qt/ui/animation_dock_widget.cpp
-           source/scwx/qt/ui/api_key_edit_widget.cpp
            source/scwx/qt/ui/collapsible_group.cpp
            source/scwx/qt/ui/county_dialog.cpp
-           source/scwx/qt/ui/custom_layer_dialog.cpp
+           source/scwx/qt/ui/wfo_dialog.cpp
            source/scwx/qt/ui/download_dialog.cpp
-           source/scwx/qt/ui/edit_line_dialog.cpp
-           source/scwx/qt/ui/edit_marker_dialog.cpp
            source/scwx/qt/ui/flow_layout.cpp
            source/scwx/qt/ui/gps_info_dialog.cpp
            source/scwx/qt/ui/hotkey_edit.cpp
@@ -309,47 +278,36 @@ set(SRC_UI source/scwx/qt/ui/about_dialog.cpp
            source/scwx/qt/ui/level2_products_widget.cpp
            source/scwx/qt/ui/level2_settings_widget.cpp
            source/scwx/qt/ui/level3_products_widget.cpp
-           source/scwx/qt/ui/line_label.cpp
            source/scwx/qt/ui/open_url_dialog.cpp
            source/scwx/qt/ui/placefile_dialog.cpp
            source/scwx/qt/ui/placefile_settings_widget.cpp
-           source/scwx/qt/ui/marker_dialog.cpp
-           source/scwx/qt/ui/marker_settings_widget.cpp
            source/scwx/qt/ui/progress_dialog.cpp
            source/scwx/qt/ui/radar_site_dialog.cpp
            source/scwx/qt/ui/settings_dialog.cpp
            source/scwx/qt/ui/serial_port_dialog.cpp
-           source/scwx/qt/ui/update_dialog.cpp
-           source/scwx/qt/ui/wfo_dialog.cpp)
+           source/scwx/qt/ui/update_dialog.cpp)
 set(UI_UI  source/scwx/qt/ui/about_dialog.ui
            source/scwx/qt/ui/alert_dialog.ui
            source/scwx/qt/ui/alert_dock_widget.ui
            source/scwx/qt/ui/animation_dock_widget.ui
            source/scwx/qt/ui/collapsible_group.ui
            source/scwx/qt/ui/county_dialog.ui
-           source/scwx/qt/ui/custom_layer_dialog.ui
-           source/scwx/qt/ui/edit_line_dialog.ui
-           source/scwx/qt/ui/edit_marker_dialog.ui
+           source/scwx/qt/ui/wfo_dialog.ui
            source/scwx/qt/ui/gps_info_dialog.ui
            source/scwx/qt/ui/imgui_debug_dialog.ui
            source/scwx/qt/ui/layer_dialog.ui
            source/scwx/qt/ui/open_url_dialog.ui
            source/scwx/qt/ui/placefile_dialog.ui
            source/scwx/qt/ui/placefile_settings_widget.ui
-           source/scwx/qt/ui/marker_dialog.ui
-           source/scwx/qt/ui/marker_settings_widget.ui
            source/scwx/qt/ui/progress_dialog.ui
            source/scwx/qt/ui/radar_site_dialog.ui
            source/scwx/qt/ui/settings_dialog.ui
            source/scwx/qt/ui/serial_port_dialog.ui
-           source/scwx/qt/ui/update_dialog.ui
-           source/scwx/qt/ui/wfo_dialog.ui)
-set(HDR_UI_SETTINGS source/scwx/qt/ui/settings/alert_palette_settings_widget.hpp
-                    source/scwx/qt/ui/settings/hotkey_settings_widget.hpp
+           source/scwx/qt/ui/update_dialog.ui)
+set(HDR_UI_SETTINGS source/scwx/qt/ui/settings/hotkey_settings_widget.hpp
                     source/scwx/qt/ui/settings/settings_page_widget.hpp
                     source/scwx/qt/ui/settings/unit_settings_widget.hpp)
-set(SRC_UI_SETTINGS source/scwx/qt/ui/settings/alert_palette_settings_widget.cpp
-                    source/scwx/qt/ui/settings/hotkey_settings_widget.cpp
+set(SRC_UI_SETTINGS source/scwx/qt/ui/settings/hotkey_settings_widget.cpp
                     source/scwx/qt/ui/settings/settings_page_widget.cpp
                     source/scwx/qt/ui/settings/unit_settings_widget.cpp)
 set(HDR_UI_SETUP source/scwx/qt/ui/setup/audio_codec_page.hpp
@@ -364,9 +322,6 @@ set(SRC_UI_SETUP source/scwx/qt/ui/setup/audio_codec_page.cpp
                  source/scwx/qt/ui/setup/map_provider_page.cpp
                  source/scwx/qt/ui/setup/setup_wizard.cpp
                  source/scwx/qt/ui/setup/welcome_page.cpp)
-set(HDR_UI_WIDGETS source/scwx/qt/ui/widgets/focused_combo_box.hpp
-                   source/scwx/qt/ui/widgets/focused_double_spin_box.hpp
-                   source/scwx/qt/ui/widgets/focused_spin_box.hpp)
 set(HDR_UTIL source/scwx/qt/util/color.hpp
              source/scwx/qt/util/file.hpp
              source/scwx/qt/util/geographic_lib.hpp
@@ -376,10 +331,8 @@ set(HDR_UTIL source/scwx/qt/util/color.hpp
              source/scwx/qt/util/network.hpp
              source/scwx/qt/util/streams.hpp
              source/scwx/qt/util/texture_atlas.hpp
-             source/scwx/qt/util/q_color_modulate.hpp
              source/scwx/qt/util/q_file_buffer.hpp
              source/scwx/qt/util/q_file_input_stream.hpp
-             source/scwx/qt/util/queue_counter.hpp
              source/scwx/qt/util/time.hpp
              source/scwx/qt/util/tooltip.hpp)
 set(SRC_UTIL source/scwx/qt/util/color.cpp
@@ -390,10 +343,8 @@ set(SRC_UTIL source/scwx/qt/util/color.cpp
              source/scwx/qt/util/maplibre.cpp
              source/scwx/qt/util/network.cpp
              source/scwx/qt/util/texture_atlas.cpp
-             source/scwx/qt/util/q_color_modulate.cpp
              source/scwx/qt/util/q_file_buffer.cpp
              source/scwx/qt/util/q_file_input_stream.cpp
-             source/scwx/qt/util/queue_counter.cpp
              source/scwx/qt/util/time.cpp
              source/scwx/qt/util/tooltip.cpp)
 set(HDR_VIEW source/scwx/qt/view/level2_product_view.hpp
@@ -434,13 +385,13 @@ set(JSON_FILES res/config/radar_sites.json)
 set(TS_FILES ts/scwx_en_US.ts)
 
 set(RADAR_SITES_FILE ${scwx-qt_SOURCE_DIR}/res/config/radar_sites.json)
-set(COUNTY_DBF_FILES ${SCWX_DIR}/data/db/c_18mr25.dbf)
-set(ZONE_DBF_FILES   ${SCWX_DIR}/data/db/fz18mr25.dbf
-                     ${SCWX_DIR}/data/db/mz18mr25.dbf
-                     ${SCWX_DIR}/data/db/oz18mr25.dbf
-                     ${SCWX_DIR}/data/db/z_18mr25.dbf)
-set(STATE_DBF_FILES  ${SCWX_DIR}/data/db/s_18mr25.dbf)
-set(WFO_DBF_FILES    ${SCWX_DIR}/data/db/w_18mr25.dbf)
+set(COUNTY_DBF_FILES ${SCWX_DIR}/data/db/c_05mr24.dbf)
+set(ZONE_DBF_FILES   ${SCWX_DIR}/data/db/fz05mr24.dbf
+                     ${SCWX_DIR}/data/db/mz05mr24.dbf
+                     ${SCWX_DIR}/data/db/oz05mr24.dbf
+                     ${SCWX_DIR}/data/db/z_05mr24.dbf)
+set(STATE_DBF_FILES  ${SCWX_DIR}/data/db/s_05mr24.dbf)
+set(WFO_DBF_FILES    ${SCWX_DIR}/data/db/w_05mr24.dbf)
 set(COUNTIES_SQLITE_DB ${scwx-qt_BINARY_DIR}/res/db/counties.db)
 
 set(RESOURCE_INPUT  ${scwx-qt_SOURCE_DIR}/res/scwx-qt.rc.in)
@@ -478,19 +429,17 @@ set(PROJECT_SOURCES ${HDR_MAIN}
                     ${SRC_UI_SETTINGS}
                     ${HDR_UI_SETUP}
                     ${SRC_UI_SETUP}
-                    ${HDR_UI_WIDGETS}
                     ${HDR_UTIL}
                     ${SRC_UTIL}
                     ${HDR_VIEW}
                     ${SRC_VIEW}
                     ${SHADER_FILES}
                     ${JSON_FILES}
+                    ${RESOURCE_FILES}
                     ${TS_FILES}
                     ${CMAKE_FILES})
 set(EXECUTABLE_SOURCES ${SRC_EXE_MAIN})
 
-qt_add_resources(PROJECT_SOURCES ${RESOURCE_FILES})
-
 source_group("Header Files\\main"         FILES ${HDR_MAIN})
 source_group("Source Files\\main"         FILES ${SRC_MAIN})
 source_group("Header Files\\config"       FILES ${HDR_CONFIG})
@@ -519,7 +468,6 @@ source_group("Header Files\\ui\\settings" FILES ${HDR_UI_SETTINGS})
 source_group("Source Files\\ui\\settings" FILES ${SRC_UI_SETTINGS})
 source_group("Header Files\\ui\\setup"    FILES ${HDR_UI_SETUP})
 source_group("Source Files\\ui\\setup"    FILES ${SRC_UI_SETUP})
-source_group("Header Files\\ui\\widgets"  FILES ${HDR_UI_WIDGETS})
 source_group("UI Files\\ui"               FILES ${UI_UI})
 source_group("Header Files\\util"         FILES ${HDR_UTIL})
 source_group("Source Files\\util"         FILES ${SRC_UTIL})
@@ -532,7 +480,6 @@ source_group("I18N Files"                 FILES ${TS_FILES})
 
 add_library(scwx-qt OBJECT ${PROJECT_SOURCES})
 set_property(TARGET scwx-qt PROPERTY AUTOMOC ON)
-set_property(TARGET scwx-qt PROPERTY AUTOGEN_ORIGIN_DEPENDS OFF)
 
 add_custom_command(OUTPUT  ${COUNTIES_SQLITE_DB}
                    COMMAND ${Python_EXECUTABLE}
@@ -579,8 +526,7 @@ else()
                                -v ${SCWX_VERSION}
                                -c ${VERSIONS_CACHE}
                                -i ${VERSIONS_INPUT}
-                               -o ${VERSIONS_HEADER}
-                               -b ${SCWX_BUILD_NUM})
+                               -o ${VERSIONS_HEADER})
 endif()
 
 add_custom_target(scwx-qt_generate_versions ALL
@@ -626,29 +572,7 @@ set_target_properties(scwx-qt_update_radar_sites   PROPERTIES FOLDER generate)
 if (WIN32)
     set(APP_ICON_RESOURCE_WINDOWS ${RESOURCE_OUTPUT})
     qt_add_executable(supercell-wx ${EXECUTABLE_SOURCES} ${APP_ICON_RESOURCE_WINDOWS})
-    if (SCWX_DISABLE_CONSOLE)
-        set_target_properties(supercell-wx PROPERTIES WIN32_EXECUTABLE $,TRUE,FALSE>)
-    endif()
-elseif (APPLE)
-    set(SCWX_ICON "${scwx-qt_SOURCE_DIR}/res/icons/scwx.icns")
-
-    set_source_files_properties(${SCWX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
-
-    qt_add_executable(supercell-wx ${EXECUTABLE_SOURCES} ${SCWX_ICON})
-
-    string(TIMESTAMP CURRENT_YEAR "%Y")
-
-    set_target_properties(supercell-wx PROPERTIES
-                          MACOSX_BUNDLE                      TRUE
-                          MACOSX_BUNDLE_INFO_PLIST           "${scwx-qt_SOURCE_DIR}/res/scwx-qt.plist.in"
-                          MACOSX_BUNDLE_GUI_IDENTIFIER       "net.supercellwx.app"
-                          MACOSX_BUNDLE_BUNDLE_NAME          "Supercell Wx"
-                          MACOSX_BUNDLE_BUNDLE_VERSION       "${SCWX_VERSION}"
-                          MACOSX_BUNDLE_SHORT_VERSION_STRING "${SCWX_VERSION}"
-                          MACOSX_BUNDLE_COPYRIGHT            "Copyright ${CURRENT_YEAR} Dan Paulat"
-                          MACOSX_BUNDLE_ICON_FILE            "scwx.icns"
-                          MACOSX_BUNDLE_INFO_STRING          "Free and open source advanced weather radar"
-                          RESOURCE                           ${SCWX_ICON})
+    set_target_properties(supercell-wx PROPERTIES WIN32_EXECUTABLE $,TRUE,FALSE>)
 else()
     qt_add_executable(supercell-wx ${EXECUTABLE_SOURCES})
 endif()
@@ -658,17 +582,12 @@ if (WIN32)
     target_compile_definitions(supercell-wx PUBLIC WIN32_LEAN_AND_MEAN)
 endif()
 
-if (LINUX)
+if (NOT MSVC)
     # Qt emit keyword is incompatible with TBB
     target_compile_definitions(scwx-qt      PRIVATE QT_NO_EMIT)
     target_compile_definitions(supercell-wx PRIVATE QT_NO_EMIT)
 endif()
 
-if (APPLE)
-    target_compile_definitions(scwx-qt      PRIVATE GL_SILENCE_DEPRECATION)
-    target_compile_definitions(supercell-wx PRIVATE GL_SILENCE_DEPRECATION)
-endif()
-
 target_include_directories(scwx-qt PUBLIC ${scwx-qt_SOURCE_DIR}/source
                                           ${FTGL_INCLUDE_DIR}
                                           ${IMGUI_INCLUDE_DIRS}
@@ -724,25 +643,6 @@ else()
     target_compile_options(supercell-wx PRIVATE "$<$:-g>")
 endif()
 
-if (LINUX)
-    # Add wayland client packages
-    find_package(QT NAMES Qt6
-                 COMPONENTS WaylandClient
-                 REQUIRED)
-
-    find_package(Qt${QT_VERSION_MAJOR}
-                 COMPONENTS WaylandClient
-                 REQUIRED)
-    target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::WaylandClient)
-endif()
-
-if (LINUX)
-    find_package(mesa-glu REQUIRED)
-    target_link_libraries(scwx-qt PUBLIC mesa-glu::mesa-glu)
-else()
-    target_link_libraries(scwx-qt PUBLIC OpenGL::GLU)
-endif()
-
 target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets
                                      Qt${QT_VERSION_MAJOR}::OpenGLWidgets
                                      Qt${QT_VERSION_MAJOR}::Multimedia
@@ -751,7 +651,6 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets
                                      Qt${QT_VERSION_MAJOR}::Svg
                                      Boost::json
                                      Boost::timer
-                                     Boost::atomic
                                      QMapLibre::Core
                                      $<$:opengl32>
                                      $<$:SetupAPI>
@@ -759,22 +658,18 @@ target_link_libraries(scwx-qt PUBLIC Qt${QT_VERSION_MAJOR}::Widgets
                                      GeographicLib::GeographicLib
                                      GEOS::geos
                                      GEOS::geos_cxx_flags
-                                     glad_gl_core_33
+                                     GLEW::GLEW
                                      glm::glm
                                      imgui
-                                     qt6ct-common
-                                     qt6ct-widgets
                                      SQLite::SQLite3
                                      wxdata)
 
 target_link_libraries(supercell-wx PRIVATE scwx-qt
                                            wxdata)
 
-if (LINUX)
-    # Set DT_RUNPATH for Linux targets
-    set_target_properties(MLNQtCore    PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") # QMapLibre::Core
-    set_target_properties(supercell-wx PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib")
-endif()
+# Set DT_RUNPATH for Linux targets
+set_target_properties(MLNQtCore    PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib") # QMapLibre::Core
+set_target_properties(supercell-wx PROPERTIES INSTALL_RPATH "\$ORIGIN/../lib")
 
 install(TARGETS supercell-wx
                 MLNQtCore # QMapLibre::Core
@@ -784,15 +679,7 @@ install(TARGETS supercell-wx
                                "^(/usr)?/lib/.*\\.so(\\..*)?"
         RUNTIME
           COMPONENT supercell-wx
-        BUNDLE
-          DESTINATION .
-          COMPONENT supercell-wx
-          OPTIONAL
         LIBRARY
-          COMPONENT supercell-wx
-          OPTIONAL
-        FRAMEWORK
-          DESTINATION Frameworks
           COMPONENT supercell-wx
           OPTIONAL)
 
@@ -814,64 +701,14 @@ install(SCRIPT ${deploy_script_qmaplibre_core}
 install(SCRIPT ${deploy_script_scwx}
         COMPONENT supercell-wx)
 
-if (APPLE)
-    # Install additional script to fix up the bundle
-    install(CODE [[
-            include (BundleUtilities)
-
-            # Define the bundle path
-            set(BUNDLE_PATH "${CMAKE_INSTALL_PREFIX}/supercell-wx.app")
-
-            file(GLOB_RECURSE PLUGIN_DYLIBS "${BUNDLE_PATH}/Contents/PlugIns/**/*.dylib")
-
-            # Add the correct rpath for plugins to find bundled frameworks
-            foreach(PLUGIN_DYLIB ${PLUGIN_DYLIBS})
-                execute_process(
-                    COMMAND install_name_tool -add_rpath "@loader_path/../../Frameworks"
-                    ${PLUGIN_DYLIB}
-                    )
-            endforeach()
-
-            # Fix up the bundle with all dependencies
-            fixup_bundle(
-                "${BUNDLE_PATH}"
-                ""
-                "${CMAKE_INSTALL_PREFIX}/lib;${CMAKE_INSTALL_PREFIX}/Frameworks"
-                )
-
-            # Re-sign the bundle
-            execute_process(
-                COMMAND codesign --force --deep --sign - "${BUNDLE_PATH}"
-                )
-
-            # Verify the bundle
-            verify_app("${BUNDLE_PATH}")
-
-            # Rename to "Supercell Wx.app"
-            file(REMOVE_RECURSE
-                 "${CMAKE_INSTALL_PREFIX}/Supercell Wx.app")
-            file(RENAME
-                 "${BUNDLE_PATH}"
-                 "${CMAKE_INSTALL_PREFIX}/Supercell Wx.app")
-
-            # Remove extra directories
-            file(REMOVE_RECURSE
-                 "${CMAKE_INSTALL_PREFIX}/Frameworks")
-            file(REMOVE_RECURSE
-                 "${CMAKE_INSTALL_PREFIX}/lib")
-            ]]
-            COMPONENT supercell-wx)
-endif()
-
-set(CPACK_PACKAGE_NAME          "Supercell Wx")
-set(CPACK_PACKAGE_VENDOR        "Dan Paulat")
-set(CPACK_PACKAGE_CHECKSUM      SHA256)
-set(CPACK_RESOURCE_FILE_LICENSE "${SCWX_DIR}/LICENSE.txt")
-
 if (MSVC)
+    set(CPACK_PACKAGE_NAME                "Supercell Wx")
+    set(CPACK_PACKAGE_VENDOR              "Dan Paulat")
     set(CPACK_PACKAGE_FILE_NAME           "supercell-wx-v${SCWX_VERSION}-windows-x64")
     set(CPACK_PACKAGE_INSTALL_DIRECTORY   "Supercell Wx")
     set(CPACK_PACKAGE_ICON                "${CMAKE_CURRENT_SOURCE_DIR}/res/icons/scwx-256.ico")
+    set(CPACK_PACKAGE_CHECKSUM            SHA256)
+    set(CPACK_RESOURCE_FILE_LICENSE       "${SCWX_DIR}/LICENSE.txt")
     set(CPACK_GENERATOR                   WIX)
     set(CPACK_PACKAGE_EXECUTABLES         "supercell-wx;Supercell Wx")
     set(CPACK_WIX_UPGRADE_GUID            36AD0F51-4D4F-4B5D-AB61-94C6B4E4FE1C)
@@ -883,15 +720,5 @@ if (MSVC)
     set(CPACK_INSTALL_CMAKE_PROJECTS
         "${CMAKE_CURRENT_BINARY_DIR};${CMAKE_PROJECT_NAME};supercell-wx;/")
 
-    include(CPack)
-elseif(APPLE)
-    set(CPACK_PACKAGE_FILE_NAME   "supercell-wx-v${SCWX_VERSION}-macos")
-    set(CPACK_PACKAGE_ICON        "${SCWX_ICON}")
-    set(CPACK_PACKAGE_VERSION     "${SCWX_VERSION}")
-
-    set(CPACK_GENERATOR DragNDrop)
-
-    set(CPACK_COMPONENTS_ALL supercell-wx)
-
     include(CPack)
 endif()
diff --git a/scwx-qt/scwx-qt.qrc b/scwx-qt/scwx-qt.qrc
index ccb2e42a..c9e00337 100644
--- a/scwx-qt/scwx-qt.qrc
+++ b/scwx-qt/scwx-qt.qrc
@@ -32,10 +32,6 @@
         res/icons/font-awesome-6/angles-up-solid.svg
         res/icons/font-awesome-6/backward-step-solid.svg
         res/icons/font-awesome-6/book-solid.svg
-        res/icons/font-awesome-6/briefcase-solid.svg
-        res/icons/font-awesome-6/building-columns-solid.svg
-        res/icons/font-awesome-6/building-solid.svg
-        res/icons/font-awesome-6/caravan-solid.svg
         res/icons/font-awesome-6/copy-regular.svg
         res/icons/font-awesome-6/discord.svg
         res/icons/font-awesome-6/earth-americas-solid.svg
@@ -44,11 +40,8 @@
         res/icons/font-awesome-6/gears-solid.svg
         res/icons/font-awesome-6/github.svg
         res/icons/font-awesome-6/house-solid.svg
-        res/icons/font-awesome-6/house-solid-white.svg
         res/icons/font-awesome-6/keyboard-regular.svg
         res/icons/font-awesome-6/layer-group-solid.svg
-        res/icons/font-awesome-6/location-crosshairs-solid.svg
-        res/icons/font-awesome-6/location-pin.svg
         res/icons/font-awesome-6/palette-solid.svg
         res/icons/font-awesome-6/pause-solid.svg
         res/icons/font-awesome-6/play-solid.svg
@@ -60,9 +53,7 @@
         res/icons/font-awesome-6/square-minus-regular.svg
         res/icons/font-awesome-6/square-plus-regular.svg
         res/icons/font-awesome-6/star-solid.svg
-        res/icons/font-awesome-6/star-solid-white.svg
         res/icons/font-awesome-6/stop-solid.svg
-        res/icons/font-awesome-6/tent-solid.svg
         res/icons/font-awesome-6/volume-high-solid.svg
         res/palettes/wct/CC.pal
         res/palettes/wct/Default16.pal
@@ -79,20 +70,11 @@
         res/palettes/wct/SW.pal
         res/palettes/wct/VIL.pal
         res/palettes/wct/ZDR.pal
-        ../external/qt6ct/colors/airy.conf
-        ../external/qt6ct/colors/darker.conf
-        ../external/qt6ct/colors/dusk.conf
-        ../external/qt6ct/colors/ia_ora.conf
-        ../external/qt6ct/colors/sand.conf
-        ../external/qt6ct/colors/simple.conf
-        ../external/qt6ct/colors/waves.conf
         res/textures/lines/default-1x7.png
         res/textures/lines/test-pattern.png
         res/textures/images/cursor-17.png
         res/textures/images/crosshairs-24.png
         res/textures/images/dot-3.png
-        res/textures/images/dot.svg
-        res/textures/images/location-marker.svg
         res/textures/images/mapbox-logo.svg
         res/textures/images/maptiler-logo.svg
     
diff --git a/scwx-qt/source/scwx/qt/config/radar_site.cpp b/scwx-qt/source/scwx/qt/config/radar_site.cpp
index 69815636..c0cb4636 100644
--- a/scwx-qt/source/scwx/qt/config/radar_site.cpp
+++ b/scwx-qt/source/scwx/qt/config/radar_site.cpp
@@ -10,7 +10,7 @@
 
 #include 
 
-#if (__cpp_lib_chrono < 201907L)
+#if !defined(_MSC_VER)
 #   include 
 #endif
 
@@ -51,7 +51,6 @@ public:
    std::string state_ {};
    std::string place_ {};
    std::string tzName_ {};
-   double      altitude_ {0.0};
 
    const scwx::util::time_zone* timeZone_ {nullptr};
 };
@@ -143,11 +142,6 @@ const scwx::util::time_zone* RadarSite::time_zone() const
    return p->timeZone_;
 }
 
-units::length::feet RadarSite::altitude() const
-{
-   return units::length::feet(p->altitude_);
-}
-
 std::shared_ptr RadarSite::Get(const std::string& id)
 {
    std::shared_lock           lock(siteMutex_);
@@ -245,7 +239,7 @@ size_t RadarSite::ReadConfig(const std::string& path)
    bool   dataValid  = true;
    size_t sitesAdded = 0;
 
-   boost::json::value j = util::json::ReadJsonQFile(path);
+   boost::json::value j = util::json::ReadJsonFile(path);
 
    dataValid = j.is_array();
 
@@ -274,12 +268,10 @@ size_t RadarSite::ReadConfig(const std::string& path)
             site->p->state_ = boost::json::value_to(o.at("state"));
             site->p->place_ = boost::json::value_to(o.at("place"));
             site->p->tzName_ = boost::json::value_to(o.at("tz"));
-            site->p->altitude_ =
-               boost::json::value_to(o.at("elevation"));
 
             try
             {
-#if (__cpp_lib_chrono >= 201907L)
+#if defined(_MSC_VER)
                using namespace std::chrono;
 #else
                using namespace date;
diff --git a/scwx-qt/source/scwx/qt/config/radar_site.hpp b/scwx-qt/source/scwx/qt/config/radar_site.hpp
index cf622d7a..16f6e710 100644
--- a/scwx-qt/source/scwx/qt/config/radar_site.hpp
+++ b/scwx-qt/source/scwx/qt/config/radar_site.hpp
@@ -6,9 +6,12 @@
 #include 
 #include 
 #include 
-#include 
 
-namespace scwx::qt::config
+namespace scwx
+{
+namespace qt
+{
+namespace config
 {
 
 class RadarSiteImpl;
@@ -25,19 +28,18 @@ public:
    RadarSite(RadarSite&&) noexcept;
    RadarSite& operator=(RadarSite&&) noexcept;
 
-   [[nodiscard]] std::string                 type() const;
-   [[nodiscard]] std::string                 type_name() const;
-   [[nodiscard]] std::string                 id() const;
-   [[nodiscard]] double                      latitude() const;
-   [[nodiscard]] double                      longitude() const;
-   [[nodiscard]] std::string                 country() const;
-   [[nodiscard]] std::string                 state() const;
-   [[nodiscard]] std::string                 place() const;
-   [[nodiscard]] std::string                 location_name() const;
-   [[nodiscard]] std::string                 tz_name() const;
-   [[nodiscard]] units::length::feet altitude() const;
+   std::string type() const;
+   std::string type_name() const;
+   std::string id() const;
+   double      latitude() const;
+   double      longitude() const;
+   std::string country() const;
+   std::string state() const;
+   std::string place() const;
+   std::string location_name() const;
+   std::string tz_name() const;
 
-   [[nodiscard]] const scwx::util::time_zone* time_zone() const;
+   const scwx::util::time_zone* time_zone() const;
 
    static std::shared_ptr              Get(const std::string& id);
    static std::vector> GetAll();
@@ -65,4 +67,6 @@ private:
 
 std::string GetRadarIdFromSiteId(const std::string& siteId);
 
-} // namespace scwx::qt::config
+} // namespace config
+} // namespace qt
+} // namespace scwx
diff --git a/scwx-qt/source/scwx/qt/gl/draw/draw_item.cpp b/scwx-qt/source/scwx/qt/gl/draw/draw_item.cpp
index 0f18e40f..c6737a10 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/draw_item.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/draw_item.cpp
@@ -30,11 +30,13 @@ static const std::string logPrefix_ = "scwx::qt::gl::draw::draw_item";
 class DrawItem::Impl
 {
 public:
-   explicit Impl() = default;
-   ~Impl()         = default;
+   explicit Impl(OpenGLFunctions& gl) : gl_ {gl} {}
+   ~Impl() {}
+
+   OpenGLFunctions& gl_;
 };
 
-DrawItem::DrawItem() : p(std::make_unique()) {}
+DrawItem::DrawItem(OpenGLFunctions& gl) : p(std::make_unique(gl)) {}
 DrawItem::~DrawItem() = default;
 
 DrawItem::DrawItem(DrawItem&&) noexcept            = default;
@@ -72,7 +74,7 @@ void DrawItem::UseDefaultProjection(
                                      0.0f,
                                      static_cast(params.height));
 
-   glUniformMatrix4fv(
+   p->gl_.glUniformMatrix4fv(
       uMVPMatrixLocation, 1, GL_FALSE, glm::value_ptr(projection));
 }
 
@@ -89,7 +91,7 @@ void DrawItem::UseRotationProjection(
                             glm::radians(params.bearing),
                             glm::vec3(0.0f, 0.0f, 1.0f));
 
-   glUniformMatrix4fv(
+   p->gl_.glUniformMatrix4fv(
       uMVPMatrixLocation, 1, GL_FALSE, glm::value_ptr(projection));
 }
 
@@ -98,14 +100,16 @@ void DrawItem::UseMapProjection(
    GLint                                         uMVPMatrixLocation,
    GLint                                         uMapScreenCoordLocation)
 {
+   OpenGLFunctions& gl = p->gl_;
+
    const glm::mat4 uMVPMatrix = util::maplibre::GetMapMatrix(params);
 
-   glUniform2fv(uMapScreenCoordLocation,
-                1,
-                glm::value_ptr(util::maplibre::LatLongToScreenCoordinate(
-                   {params.latitude, params.longitude})));
+   gl.glUniform2fv(uMapScreenCoordLocation,
+                   1,
+                   glm::value_ptr(util::maplibre::LatLongToScreenCoordinate(
+                      {params.latitude, params.longitude})));
 
-   glUniformMatrix4fv(
+   gl.glUniformMatrix4fv(
       uMVPMatrixLocation, 1, GL_FALSE, glm::value_ptr(uMVPMatrix));
 }
 
diff --git a/scwx-qt/source/scwx/qt/gl/draw/draw_item.hpp b/scwx-qt/source/scwx/qt/gl/draw/draw_item.hpp
index 28350190..f7df44c4 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/draw_item.hpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/draw_item.hpp
@@ -21,8 +21,8 @@ namespace draw
 class DrawItem
 {
 public:
-   explicit DrawItem();
-   virtual ~DrawItem();
+   explicit DrawItem(OpenGLFunctions& gl);
+   ~DrawItem();
 
    DrawItem(const DrawItem&)            = delete;
    DrawItem& operator=(const DrawItem&) = delete;
diff --git a/scwx-qt/source/scwx/qt/gl/draw/geo_icons.cpp b/scwx-qt/source/scwx/qt/gl/draw/geo_icons.cpp
index 05cc26a5..622718e3 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/geo_icons.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/geo_icons.cpp
@@ -4,7 +4,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 
@@ -39,7 +38,7 @@ static constexpr std::size_t kIntegersPerVertex_ = 4;
 static constexpr std::size_t kIntegerBufferLength_ =
    kNumTriangles * kVerticesPerTriangle * kIntegersPerVertex_;
 
-struct GeoIconDrawItem : types::EventHandler
+struct GeoIconDrawItem
 {
    units::length::nautical_miles       threshold_ {};
    std::chrono::sys_time startTime_ {};
@@ -145,7 +144,7 @@ public:
 };
 
 GeoIcons::GeoIcons(const std::shared_ptr& context) :
-    DrawItem(), p(std::make_unique(context))
+    DrawItem(context->gl()), p(std::make_unique(context))
 {
 }
 GeoIcons::~GeoIcons() = default;
@@ -166,6 +165,8 @@ void GeoIcons::set_thresholded(bool thresholded)
 
 void GeoIcons::Initialize()
 {
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
    p->shaderProgram_ = p->context_->GetShaderProgram(
       {{GL_VERTEX_SHADER, ":/gl/geo_texture2d.vert"},
        {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"},
@@ -180,95 +181,88 @@ void GeoIcons::Initialize()
    p->uSelectedTimeLocation_ =
       p->shaderProgram_->GetUniformLocation("uSelectedTime");
 
-   glGenVertexArrays(1, &p->vao_);
-   glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl.glGenVertexArrays(1, &p->vao_);
+   gl.glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
-   glBindVertexArray(p->vao_);
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
-
-   // NOLINTBEGIN(modernize-use-nullptr)
-   // NOLINTBEGIN(performance-no-int-to-ptr)
-   // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
+   gl.glBindVertexArray(p->vao_);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aLatLong
-   glVertexAttribPointer(0,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(0);
+   gl.glVertexAttribPointer(0,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(0);
 
    // aXYOffset
-   glVertexAttribPointer(1,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(2 * sizeof(float)));
-   glEnableVertexAttribArray(1);
+   gl.glVertexAttribPointer(1,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(2 * sizeof(float)));
+   gl.glEnableVertexAttribArray(1);
 
    // aModulate
-   glVertexAttribPointer(3,
-                         4,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(4 * sizeof(float)));
-   glEnableVertexAttribArray(3);
+   gl.glVertexAttribPointer(3,
+                            4,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(4 * sizeof(float)));
+   gl.glEnableVertexAttribArray(3);
 
    // aAngle
-   glVertexAttribPointer(4,
-                         1,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(8 * sizeof(float)));
-   glEnableVertexAttribArray(4);
+   gl.glVertexAttribPointer(4,
+                            1,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(8 * sizeof(float)));
+   gl.glEnableVertexAttribArray(4);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aTexCoord
-   glVertexAttribPointer(2,
-                         3,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerTexCoord * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(2);
+   gl.glVertexAttribPointer(2,
+                            3,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerTexCoord * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(2);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[2]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[2]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aThreshold
-   glVertexAttribIPointer(5, //
-                          1,
-                          GL_INT,
-                          0,
-                          static_cast(0));
-   glEnableVertexAttribArray(5);
+   gl.glVertexAttribIPointer(5, //
+                             1,
+                             GL_INT,
+                             0,
+                             static_cast(0));
+   gl.glEnableVertexAttribArray(5);
 
    // aTimeRange
-   glVertexAttribIPointer(6, //
-                          2,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          reinterpret_cast(1 * sizeof(GLint)));
-   glEnableVertexAttribArray(6);
+   gl.glVertexAttribIPointer(6, //
+                             2,
+                             GL_INT,
+                             kIntegersPerVertex_ * sizeof(GLint),
+                             reinterpret_cast(1 * sizeof(GLint)));
+   gl.glEnableVertexAttribArray(6);
 
    // aDisplayed
-   glVertexAttribIPointer(7,
-                          1,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          reinterpret_cast(3 * sizeof(GLint)));
-   glEnableVertexAttribArray(7);
-
-   // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
-   // NOLINTEND(performance-no-int-to-ptr)
-   // NOLINTEND(modernize-use-nullptr)
+   gl.glVertexAttribPointer(7,
+                            1,
+                            GL_INT,
+                            GL_FALSE,
+                            kIntegersPerVertex_ * sizeof(GLint),
+                            reinterpret_cast(3 * sizeof(float)));
+   gl.glEnableVertexAttribArray(7);
 
    p->dirty_ = true;
 }
@@ -290,7 +284,9 @@ void GeoIcons::Render(const QMapLibre::CustomLayerRenderParameters& params,
 
    if (!p->currentIconList_.empty())
    {
-      glBindVertexArray(p->vao_);
+      gl::OpenGLFunctions& gl = p->context_->gl();
+
+      gl.glBindVertexArray(p->vao_);
 
       p->Update(textureAtlasChanged);
       p->shaderProgram_->Use();
@@ -303,38 +299,40 @@ void GeoIcons::Render(const QMapLibre::CustomLayerRenderParameters& params,
          // If thresholding is enabled, set the map distance
          units::length::nautical_miles mapDistance =
             util::maplibre::GetMapDistance(params);
-         glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
+         gl.glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
       }
       else
       {
          // If thresholding is disabled, set the map distance to 0
-         glUniform1f(p->uMapDistanceLocation_, 0.0f);
+         gl.glUniform1f(p->uMapDistanceLocation_, 0.0f);
       }
 
       // Selected time
       std::chrono::system_clock::time_point selectedTime =
          (p->selectedTime_ == std::chrono::system_clock::time_point {}) ?
-            scwx::util::time::now() :
+            std::chrono::system_clock::now() :
             p->selectedTime_;
-      glUniform1i(
+      gl.glUniform1i(
          p->uSelectedTimeLocation_,
          static_cast(std::chrono::duration_cast(
                                selectedTime.time_since_epoch())
                                .count()));
 
       // Interpolate texture coordinates
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
       // Draw icons
-      glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
+      gl.glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
    }
 }
 
 void GeoIcons::Deinitialize()
 {
-   glDeleteVertexArrays(1, &p->vao_);
-   glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
+   gl.glDeleteVertexArrays(1, &p->vao_);
+   gl.glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
    std::unique_lock lock {p->iconMutex_};
 
@@ -694,7 +692,7 @@ void GeoIcons::Impl::UpdateSingleBuffer(
                                hoverIcons.end(),
                                [&di](auto& entry) { return entry.di_ == di; });
 
-   if (di->visible_ && (!di->hoverText_.empty() || di->event_ != nullptr))
+   if (di->visible_ && !di->hoverText_.empty())
    {
       const units::angle::radians radians = angle;
 
@@ -850,6 +848,8 @@ void GeoIcons::Impl::UpdateModifiedIconBuffers()
 
 void GeoIcons::Impl::Update(bool textureAtlasChanged)
 {
+   gl::OpenGLFunctions& gl = context_->gl();
+
    UpdateModifiedIconBuffers();
 
    // If the texture atlas has changed
@@ -865,12 +865,11 @@ void GeoIcons::Impl::Update(bool textureAtlasChanged)
       UpdateTextureBuffer();
 
       // Buffer texture data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * textureBuffer_.size()),
-         textureBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * textureBuffer_.size(),
+                      textureBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       lastTextureAtlasChanged_ = false;
    }
@@ -879,20 +878,18 @@ void GeoIcons::Impl::Update(bool textureAtlasChanged)
    if (dirty_)
    {
       // Buffer vertex data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * currentIconBuffer_.size()),
-         currentIconBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * currentIconBuffer_.size(),
+                      currentIconBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       // Buffer threshold data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[2]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(GLint) * currentIntegerBuffer_.size()),
-         currentIntegerBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[2]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(GLint) * currentIntegerBuffer_.size(),
+                      currentIntegerBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       numVertices_ =
          static_cast(currentIconBuffer_.size() / kPointsPerVertex);
@@ -907,7 +904,7 @@ bool GeoIcons::RunMousePicking(
    const QPointF&   mouseGlobalPos,
    const glm::vec2& mouseCoords,
    const common::Coordinate& /* mouseGeoCoords */,
-   std::shared_ptr& eventHandler)
+   std::shared_ptr& /* eventHandler */)
 {
    std::unique_lock lock {p->iconMutex_};
 
@@ -931,7 +928,7 @@ bool GeoIcons::RunMousePicking(
    // If no time has been selected, use the current time
    std::chrono::system_clock::time_point selectedTime =
       (p->selectedTime_ == std::chrono::system_clock::time_point {}) ?
-         scwx::util::time::now() :
+         std::chrono::system_clock::now() :
          p->selectedTime_;
 
    // For each pickable icon
@@ -950,10 +947,8 @@ bool GeoIcons::RunMousePicking(
                    units::length::nautical_miles {icon.di_->threshold_}
                       .value())) < 999 &&
 
-                // Map distance is beyond/within the threshold
-                icon.di_->threshold_ < mapDistance &&
-                (icon.di_->threshold_.value() >= 0.0 ||
-                 -(icon.di_->threshold_) > mapDistance)) ||
+                // Map distance is beyond the threshold
+                icon.di_->threshold_ < mapDistance) ||
 
              (
                 // Geo icon has a start time
@@ -997,27 +992,12 @@ bool GeoIcons::RunMousePicking(
    if (it != p->currentHoverIcons_.crend())
    {
       itemPicked = true;
-      if (!it->di_->hoverText_.empty())
-      {
-         // Show tooltip
-         util::tooltip::Show(it->di_->hoverText_, mouseGlobalPos);
-      }
-      if (it->di_->event_ != nullptr)
-      {
-         eventHandler = it->di_;
-      }
+      util::tooltip::Show(it->di_->hoverText_, mouseGlobalPos);
    }
 
    return itemPicked;
 }
 
-void GeoIcons::RegisterEventHandler(
-   const std::shared_ptr& di,
-   const std::function&     eventHandler)
-{
-   di->event_ = eventHandler;
-}
-
 } // namespace draw
 } // namespace gl
 } // namespace qt
diff --git a/scwx-qt/source/scwx/qt/gl/draw/geo_icons.hpp b/scwx-qt/source/scwx/qt/gl/draw/geo_icons.hpp
index 073fc118..4d819681 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/geo_icons.hpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/geo_icons.hpp
@@ -183,16 +183,6 @@ public:
     */
    void FinishIcons();
 
-   /**
-    * Registers an event handler for an icon.
-    *
-    * @param [in] di Icon draw item
-    * @param [in] eventHandler Event handler function
-    */
-   static void
-   RegisterEventHandler(const std::shared_ptr& di,
-                        const std::function&     eventHandler);
-
 private:
    class Impl;
 
diff --git a/scwx-qt/source/scwx/qt/gl/draw/geo_lines.cpp b/scwx-qt/source/scwx/qt/gl/draw/geo_lines.cpp
index e35cca4f..d9f2c023 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/geo_lines.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/geo_lines.cpp
@@ -3,7 +3,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 
@@ -51,7 +50,6 @@ struct GeoLineDrawItem : types::EventHandler
    units::angle::degrees angle_ {};
    std::string                  hoverText_ {};
    GeoLines::HoverCallback      hoverCallback_ {nullptr};
-   size_t                       lineIndex_ {0};
 };
 
 class GeoLines::Impl
@@ -89,10 +87,10 @@ public:
    void UpdateBuffers();
    void UpdateModifiedLineBuffers();
    void UpdateSingleBuffer(const std::shared_ptr& di,
+                           std::size_t                             lineIndex,
                            std::vector&                     linesBuffer,
-                           std::vector&                 integerBuffer,
-                           std::unordered_map,
-                                              LineHoverEntry>& hoverLines);
+                           std::vector&          integerBuffer,
+                           std::vector& hoverLines);
 
    std::shared_ptr context_;
 
@@ -114,10 +112,8 @@ public:
    std::vector newLinesBuffer_ {};
    std::vector newIntegerBuffer_ {};
 
-   std::unordered_map, LineHoverEntry>
-      currentHoverLines_ {};
-   std::unordered_map, LineHoverEntry>
-      newHoverLines_ {};
+   std::vector currentHoverLines_ {};
+   std::vector newHoverLines_ {};
 
    std::shared_ptr shaderProgram_;
    GLint                          uMVPMatrixLocation_;
@@ -131,7 +127,7 @@ public:
 };
 
 GeoLines::GeoLines(std::shared_ptr context) :
-    DrawItem(), p(std::make_unique(context))
+    DrawItem(context->gl()), p(std::make_unique(context))
 {
 }
 GeoLines::~GeoLines() = default;
@@ -152,6 +148,8 @@ void GeoLines::set_thresholded(bool thresholded)
 
 void GeoLines::Initialize()
 {
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
    p->shaderProgram_ = p->context_->GetShaderProgram(
       {{GL_VERTEX_SHADER, ":/gl/geo_texture2d.vert"},
        {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"},
@@ -166,86 +164,79 @@ void GeoLines::Initialize()
    p->uSelectedTimeLocation_ =
       p->shaderProgram_->GetUniformLocation("uSelectedTime");
 
-   glGenVertexArrays(1, &p->vao_);
-   glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl.glGenVertexArrays(1, &p->vao_);
+   gl.glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
-   glBindVertexArray(p->vao_);
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
-   glBufferData(GL_ARRAY_BUFFER,
-                sizeof(float) * kLineBufferLength_,
-                nullptr,
-                GL_DYNAMIC_DRAW);
-
-   // NOLINTBEGIN(modernize-use-nullptr)
-   // NOLINTBEGIN(performance-no-int-to-ptr)
-   // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
+   gl.glBindVertexArray(p->vao_);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
+   gl.glBufferData(GL_ARRAY_BUFFER,
+                   sizeof(float) * kLineBufferLength_,
+                   nullptr,
+                   GL_DYNAMIC_DRAW);
 
    // aLatLong
-   glVertexAttribPointer(0,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(0);
+   gl.glVertexAttribPointer(0,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(0);
 
    // aXYOffset
-   glVertexAttribPointer(1,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(2 * sizeof(float)));
-   glEnableVertexAttribArray(1);
+   gl.glVertexAttribPointer(1,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(2 * sizeof(float)));
+   gl.glEnableVertexAttribArray(1);
 
    // aModulate
-   glVertexAttribPointer(3,
-                         4,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(4 * sizeof(float)));
-   glEnableVertexAttribArray(3);
+   gl.glVertexAttribPointer(3,
+                            4,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(4 * sizeof(float)));
+   gl.glEnableVertexAttribArray(3);
 
    // aAngle
-   glVertexAttribPointer(4,
-                         1,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(8 * sizeof(float)));
-   glEnableVertexAttribArray(4);
+   gl.glVertexAttribPointer(4,
+                            1,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(8 * sizeof(float)));
+   gl.glEnableVertexAttribArray(4);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aThreshold
-   glVertexAttribIPointer(5, //
-                          1,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          static_cast(0));
-   glEnableVertexAttribArray(5);
+   gl.glVertexAttribIPointer(5, //
+                             1,
+                             GL_INT,
+                             kIntegersPerVertex_ * sizeof(GLint),
+                             static_cast(0));
+   gl.glEnableVertexAttribArray(5);
 
    // aTimeRange
-   glVertexAttribIPointer(6, //
-                          2,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          reinterpret_cast(1 * sizeof(GLint)));
-   glEnableVertexAttribArray(6);
+   gl.glVertexAttribIPointer(6, //
+                             2,
+                             GL_INT,
+                             kIntegersPerVertex_ * sizeof(GLint),
+                             reinterpret_cast(1 * sizeof(GLint)));
+   gl.glEnableVertexAttribArray(6);
 
    // aDisplayed
-   glVertexAttribIPointer(7,
-                          1,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          reinterpret_cast(3 * sizeof(GLint)));
-   glEnableVertexAttribArray(7);
-
-   // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
-   // NOLINTEND(performance-no-int-to-ptr)
-   // NOLINTEND(modernize-use-nullptr)
+   gl.glVertexAttribPointer(7,
+                            1,
+                            GL_INT,
+                            GL_FALSE,
+                            kIntegersPerVertex_ * sizeof(GLint),
+                            reinterpret_cast(3 * sizeof(float)));
+   gl.glEnableVertexAttribArray(7);
 
    p->dirty_ = true;
 }
@@ -261,7 +252,9 @@ void GeoLines::Render(const QMapLibre::CustomLayerRenderParameters& params)
 
    if (p->newLineList_.size() > 0)
    {
-      glBindVertexArray(p->vao_);
+      gl::OpenGLFunctions& gl = p->context_->gl();
+
+      gl.glBindVertexArray(p->vao_);
 
       p->Update();
       p->shaderProgram_->Use();
@@ -274,37 +267,39 @@ void GeoLines::Render(const QMapLibre::CustomLayerRenderParameters& params)
          // If thresholding is enabled, set the map distance
          units::length::nautical_miles mapDistance =
             util::maplibre::GetMapDistance(params);
-         glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
+         gl.glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
       }
       else
       {
          // If thresholding is disabled, set the map distance to 0
-         glUniform1f(p->uMapDistanceLocation_, 0.0f);
+         gl.glUniform1f(p->uMapDistanceLocation_, 0.0f);
       }
 
       // Selected time
       std::chrono::system_clock::time_point selectedTime =
          (p->selectedTime_ == std::chrono::system_clock::time_point {}) ?
-            scwx::util::time::now() :
+            std::chrono::system_clock::now() :
             p->selectedTime_;
-      glUniform1i(
+      gl.glUniform1i(
          p->uSelectedTimeLocation_,
          static_cast(std::chrono::duration_cast(
                                selectedTime.time_since_epoch())
                                .count()));
 
       // Draw icons
-      glDrawArrays(GL_TRIANGLES,
-                   0,
-                   static_cast(p->currentLineList_.size() *
-                                        kVerticesPerRectangle));
+      gl.glDrawArrays(GL_TRIANGLES,
+                      0,
+                      static_cast(p->currentLineList_.size() *
+                                           kVerticesPerRectangle));
    }
 }
 
 void GeoLines::Deinitialize()
 {
-   glDeleteVertexArrays(1, &p->vao_);
-   glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
+   gl.glDeleteVertexArrays(1, &p->vao_);
+   gl.glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
    std::unique_lock lock {p->lineMutex_};
 
@@ -329,9 +324,7 @@ void GeoLines::StartLines()
 
 std::shared_ptr GeoLines::AddLine()
 {
-   auto& di = p->newLineList_.emplace_back(std::make_shared());
-   di->lineIndex_ = p->newLineList_.size() - 1;
-   return di;
+   return p->newLineList_.emplace_back(std::make_shared());
 }
 
 void GeoLines::SetLineLocation(const std::shared_ptr& di,
@@ -478,7 +471,7 @@ void GeoLines::Impl::UpdateBuffers()
 
       // Update line buffer
       UpdateSingleBuffer(
-         di, newLinesBuffer_, newIntegerBuffer_, newHoverLines_);
+         di, i, newLinesBuffer_, newIntegerBuffer_, newHoverLines_);
    }
 
    // All lines have been updated
@@ -496,15 +489,23 @@ void GeoLines::Impl::UpdateModifiedLineBuffers()
    // Update buffers for modified lines
    for (auto& di : dirtyLines_)
    {
-      // Check if modified line is in the current list
-      if (di->lineIndex_ >= currentLineList_.size() ||
-          currentLineList_[di->lineIndex_] != di)
+      // Find modified line in the current list
+      auto it =
+         std::find(currentLineList_.cbegin(), currentLineList_.cend(), di);
+
+      // Ignore invalid lines
+      if (it == currentLineList_.cend())
       {
          continue;
       }
 
-      UpdateSingleBuffer(
-         di, currentLinesBuffer_, currentIntegerBuffer_, currentHoverLines_);
+      auto lineIndex = std::distance(currentLineList_.cbegin(), it);
+
+      UpdateSingleBuffer(di,
+                         lineIndex,
+                         currentLinesBuffer_,
+                         currentIntegerBuffer_,
+                         currentHoverLines_);
    }
 
    // Clear list of modified lines
@@ -517,10 +518,10 @@ void GeoLines::Impl::UpdateModifiedLineBuffers()
 
 void GeoLines::Impl::UpdateSingleBuffer(
    const std::shared_ptr& di,
+   std::size_t                             lineIndex,
    std::vector&                     lineBuffer,
    std::vector&                     integerBuffer,
-   std::unordered_map, LineHoverEntry>&
-      hoverLines)
+   std::vector&            hoverLines)
 {
    // Threshold value
    units::length::nautical_miles threshold = di->threshold_;
@@ -588,10 +589,10 @@ void GeoLines::Impl::UpdateSingleBuffer(
 
    // Buffer position data
    auto lineBufferPosition = lineBuffer.end();
-   auto lineBufferOffset   = di->lineIndex_ * kLineBufferLength_;
+   auto lineBufferOffset   = lineIndex * kLineBufferLength_;
 
    auto integerBufferPosition = integerBuffer.end();
-   auto integerBufferOffset   = di->lineIndex_ * kIntegerBufferLength_;
+   auto integerBufferOffset   = lineIndex * kIntegerBufferLength_;
 
    if (lineBufferOffset < lineBuffer.size())
    {
@@ -620,7 +621,9 @@ void GeoLines::Impl::UpdateSingleBuffer(
       std::copy(integerData.begin(), integerData.end(), integerBufferPosition);
    }
 
-   auto hoverIt = hoverLines.find(di);
+   auto hoverIt = std::find_if(hoverLines.begin(),
+                               hoverLines.end(),
+                               [&di](auto& entry) { return entry.di_ == di; });
 
    if (di->visible_ && (!di->hoverText_.empty() ||
                         di->hoverCallback_ != nullptr || di->event_ != nullptr))
@@ -642,23 +645,17 @@ void GeoLines::Impl::UpdateSingleBuffer(
 
       if (hoverIt == hoverLines.end())
       {
-         hoverLines.emplace(di,
-                            LineHoverEntry {.di_  = di,
-                                            .p1_  = sc1,
-                                            .p2_  = sc2,
-                                            .otl_ = otl,
-                                            .otr_ = otr,
-                                            .obl_ = obl,
-                                            .obr_ = obr});
+         hoverLines.emplace_back(
+            LineHoverEntry {di, sc1, sc2, otl, otr, obl, obr});
       }
       else
       {
-         hoverIt->second.p1_  = sc1;
-         hoverIt->second.p2_  = sc2;
-         hoverIt->second.otl_ = otl;
-         hoverIt->second.otr_ = otr;
-         hoverIt->second.obl_ = obl;
-         hoverIt->second.obr_ = obr;
+         hoverIt->p1_  = sc1;
+         hoverIt->p2_  = sc2;
+         hoverIt->otl_ = otl;
+         hoverIt->otr_ = otr;
+         hoverIt->obl_ = obl;
+         hoverIt->obr_ = obr;
       }
    }
    else if (hoverIt != hoverLines.end())
@@ -674,21 +671,21 @@ void GeoLines::Impl::Update()
    // If the lines have been updated
    if (dirty_)
    {
+      gl::OpenGLFunctions& gl = context_->gl();
+
       // Buffer lines data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * currentLinesBuffer_.size()),
-         currentLinesBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * currentLinesBuffer_.size(),
+                      currentLinesBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       // Buffer threshold data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(GLint) * currentIntegerBuffer_.size()),
-         currentIntegerBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(GLint) * currentIntegerBuffer_.size(),
+                      currentIntegerBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
    }
 
    dirty_ = false;
@@ -724,18 +721,16 @@ bool GeoLines::RunMousePicking(
    // If no time has been selected, use the current time
    std::chrono::system_clock::time_point selectedTime =
       (p->selectedTime_ == std::chrono::system_clock::time_point {}) ?
-         scwx::util::time::now() :
+         std::chrono::system_clock::now() :
          p->selectedTime_;
 
    // For each pickable line
    auto it = std::find_if(
       std::execution::par_unseq,
-      p->currentHoverLines_.cbegin(),
-      p->currentHoverLines_.cend(),
-      [&mapDistance, &selectedTime, &mapMatrix, &mouseCoords](
-         const auto& lineIt)
+      p->currentHoverLines_.rbegin(),
+      p->currentHoverLines_.rend(),
+      [&mapDistance, &selectedTime, &mapMatrix, &mouseCoords](const auto& line)
       {
-         const auto& line = lineIt.second;
          if ((
                 // Placefile is thresholded
                 mapDistance > units::length::meters {0.0} &&
@@ -745,10 +740,8 @@ bool GeoLines::RunMousePicking(
                    units::length::nautical_miles {line.di_->threshold_}
                       .value())) < 999 &&
 
-                // Map distance is beyond/within the threshold
-                line.di_->threshold_ < mapDistance &&
-                (line.di_->threshold_.value() >= 0.0 ||
-                 -(line.di_->threshold_) > mapDistance)) ||
+                // Map distance is beyond the threshold
+                line.di_->threshold_ < mapDistance) ||
 
              (
                 // Line has a start time
@@ -791,24 +784,24 @@ bool GeoLines::RunMousePicking(
          return util::maplibre::IsPointInPolygon({tl, bl, br, tr}, mouseCoords);
       });
 
-   if (it != p->currentHoverLines_.cend())
+   if (it != p->currentHoverLines_.crend())
    {
       itemPicked = true;
 
-      if (!it->second.di_->hoverText_.empty())
+      if (!it->di_->hoverText_.empty())
       {
          // Show tooltip
-         util::tooltip::Show(it->second.di_->hoverText_, mouseGlobalPos);
+         util::tooltip::Show(it->di_->hoverText_, mouseGlobalPos);
       }
-      else if (it->second.di_->hoverCallback_ != nullptr)
+      else if (it->di_->hoverCallback_ != nullptr)
       {
-         it->second.di_->hoverCallback_(it->second.di_, mouseGlobalPos);
+         it->di_->hoverCallback_(it->di_, mouseGlobalPos);
       }
 
-      if (it->second.di_->event_ != nullptr)
+      if (it->di_->event_ != nullptr)
       {
          // Register event handler
-         eventHandler = it->second.di_;
+         eventHandler = it->di_;
       }
    }
 
diff --git a/scwx-qt/source/scwx/qt/gl/draw/geo_lines.hpp b/scwx-qt/source/scwx/qt/gl/draw/geo_lines.hpp
index d6727110..b7b792d0 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/geo_lines.hpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/geo_lines.hpp
@@ -19,8 +19,9 @@ struct GeoLineDrawItem;
 class GeoLines : public DrawItem
 {
 public:
-   using HoverCallback = std::function&, const QPointF&)>;
+   typedef std::function&,
+                              const QPointF&)>
+      HoverCallback;
 
    explicit GeoLines(std::shared_ptr context);
    ~GeoLines();
diff --git a/scwx-qt/source/scwx/qt/gl/draw/icons.cpp b/scwx-qt/source/scwx/qt/gl/draw/icons.cpp
index b72f4443..473b8a79 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/icons.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/icons.cpp
@@ -21,11 +21,12 @@ namespace draw
 static const std::string logPrefix_ = "scwx::qt::gl::draw::icons";
 static const auto        logger_    = scwx::util::Logger::Create(logPrefix_);
 
-static constexpr std::size_t kNumRectangles       = 1;
-static constexpr std::size_t kNumTriangles        = kNumRectangles * 2;
-static constexpr std::size_t kVerticesPerTriangle = 3;
-static constexpr std::size_t kPointsPerVertex     = 10;
-static constexpr std::size_t kPointsPerTexCoord   = 3;
+static constexpr std::size_t kNumRectangles        = 1;
+static constexpr std::size_t kNumTriangles         = kNumRectangles * 2;
+static constexpr std::size_t kVerticesPerTriangle  = 3;
+static constexpr std::size_t kVerticesPerRectangle = kVerticesPerTriangle * 2;
+static constexpr std::size_t kPointsPerVertex      = 10;
+static constexpr std::size_t kPointsPerTexCoord    = 3;
 static constexpr std::size_t kIconBufferLength =
    kNumTriangles * kVerticesPerTriangle * kPointsPerVertex;
 static constexpr std::size_t kTextureBufferLength =
@@ -116,7 +117,7 @@ public:
 };
 
 Icons::Icons(const std::shared_ptr& context) :
-    DrawItem(), p(std::make_unique(context))
+    DrawItem(context->gl()), p(std::make_unique(context))
 {
 }
 Icons::~Icons() = default;
@@ -126,6 +127,8 @@ Icons& Icons::operator=(Icons&&) noexcept = default;
 
 void Icons::Initialize()
 {
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
    p->shaderProgram_ = p->context_->GetShaderProgram(
       {{GL_VERTEX_SHADER, ":/gl/texture2d_array.vert"},
        {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"},
@@ -133,77 +136,69 @@ void Icons::Initialize()
 
    p->uMVPMatrixLocation_ = p->shaderProgram_->GetUniformLocation("uMVPMatrix");
 
-   glGenVertexArrays(1, &p->vao_);
-   glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl.glGenVertexArrays(1, &p->vao_);
+   gl.glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
-   glBindVertexArray(p->vao_);
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
-
-   // NOLINTBEGIN(modernize-use-nullptr)
-   // NOLINTBEGIN(performance-no-int-to-ptr)
-   // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
+   gl.glBindVertexArray(p->vao_);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aVertex
-   glVertexAttribPointer(0,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(0));
-   glEnableVertexAttribArray(0);
+   gl.glVertexAttribPointer(0,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(0));
+   gl.glEnableVertexAttribArray(0);
 
    // aXYOffset
-   glVertexAttribPointer(1,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(2 * sizeof(float)));
-   glEnableVertexAttribArray(1);
+   gl.glVertexAttribPointer(1,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(2 * sizeof(float)));
+   gl.glEnableVertexAttribArray(1);
 
    // aModulate
-   glVertexAttribPointer(3,
-                         4,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(4 * sizeof(float)));
-   glEnableVertexAttribArray(3);
+   gl.glVertexAttribPointer(3,
+                            4,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(4 * sizeof(float)));
+   gl.glEnableVertexAttribArray(3);
 
    // aAngle
-   glVertexAttribPointer(4,
-                         1,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(8 * sizeof(float)));
-   glEnableVertexAttribArray(4);
+   gl.glVertexAttribPointer(4,
+                            1,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(8 * sizeof(float)));
+   gl.glEnableVertexAttribArray(4);
 
    // aDisplayed
-   glVertexAttribPointer(5,
-                         1,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(9 * sizeof(float)));
-   glEnableVertexAttribArray(5);
+   gl.glVertexAttribPointer(5,
+                            1,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(9 * sizeof(float)));
+   gl.glEnableVertexAttribArray(5);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aTexCoord
-   glVertexAttribPointer(2,
-                         3,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerTexCoord * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(2);
-
-   // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
-   // NOLINTEND(performance-no-int-to-ptr)
-   // NOLINTEND(modernize-use-nullptr)
+   gl.glVertexAttribPointer(2,
+                            3,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerTexCoord * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(2);
 
    p->dirty_ = true;
 }
@@ -225,25 +220,29 @@ void Icons::Render(const QMapLibre::CustomLayerRenderParameters& params,
 
    if (!p->currentIconList_.empty())
    {
-      glBindVertexArray(p->vao_);
+      gl::OpenGLFunctions& gl = p->context_->gl();
+
+      gl.glBindVertexArray(p->vao_);
 
       p->Update(textureAtlasChanged);
       p->shaderProgram_->Use();
       UseDefaultProjection(params, p->uMVPMatrixLocation_);
 
       // Interpolate texture coordinates
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
       // Draw icons
-      glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
+      gl.glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
    }
 }
 
 void Icons::Deinitialize()
 {
-   glDeleteVertexArrays(1, &p->vao_);
-   glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
+   gl.glDeleteVertexArrays(1, &p->vao_);
+   gl.glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
    std::unique_lock lock {p->iconMutex_};
 
@@ -681,6 +680,8 @@ void Icons::Impl::UpdateModifiedIconBuffers()
 
 void Icons::Impl::Update(bool textureAtlasChanged)
 {
+   gl::OpenGLFunctions& gl = context_->gl();
+
    UpdateModifiedIconBuffers();
 
    // If the texture atlas has changed
@@ -696,12 +697,11 @@ void Icons::Impl::Update(bool textureAtlasChanged)
       UpdateTextureBuffer();
 
       // Buffer texture data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * textureBuffer_.size()),
-         textureBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * textureBuffer_.size(),
+                      textureBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       lastTextureAtlasChanged_ = false;
    }
@@ -710,12 +710,11 @@ void Icons::Impl::Update(bool textureAtlasChanged)
    if (dirty_)
    {
       // Buffer vertex data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * currentIconBuffer_.size()),
-         currentIconBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * currentIconBuffer_.size(),
+                      currentIconBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       numVertices_ =
          static_cast(currentIconBuffer_.size() / kPointsPerVertex);
@@ -742,7 +741,7 @@ bool Icons::RunMousePicking(
 
    // For each pickable icon
    auto it = std::find_if( //
-      std::execution::par,
+      std::execution::par_unseq,
       p->currentHoverIcons_.crbegin(),
       p->currentHoverIcons_.crend(),
       [&mouseLocalCoords](const auto& icon)
diff --git a/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp b/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp
index dfc3dd07..3dbab117 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp
@@ -63,12 +63,14 @@ class LinkedVectors::Impl
 {
 public:
    explicit Impl(std::shared_ptr context) :
-       geoLines_ {std::make_shared(context)}
+       context_ {context}, geoLines_ {std::make_shared(context)}
    {
    }
 
    ~Impl() {}
 
+   std::shared_ptr context_;
+
    bool borderEnabled_ {true};
    bool visible_ {true};
 
@@ -77,7 +79,7 @@ public:
 };
 
 LinkedVectors::LinkedVectors(std::shared_ptr context) :
-    DrawItem(), p(std::make_unique(context))
+    DrawItem(context->gl()), p(std::make_unique(context))
 {
 }
 LinkedVectors::~LinkedVectors() = default;
diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp
index 21dd25c2..abc852f8 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_icons.cpp
@@ -3,7 +3,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 
@@ -141,7 +140,7 @@ public:
 };
 
 PlacefileIcons::PlacefileIcons(const std::shared_ptr& context) :
-    DrawItem(), p(std::make_unique(context))
+    DrawItem(context->gl()), p(std::make_unique(context))
 {
 }
 PlacefileIcons::~PlacefileIcons() = default;
@@ -162,6 +161,9 @@ void PlacefileIcons::set_thresholded(bool thresholded)
 
 void PlacefileIcons::Initialize()
 {
+   gl::OpenGLFunctions& gl   = p->context_->gl();
+   auto&                gl30 = p->context_->gl30();
+
    p->shaderProgram_ = p->context_->GetShaderProgram(
       {{GL_VERTEX_SHADER, ":/gl/geo_texture2d.vert"},
        {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"},
@@ -176,90 +178,82 @@ void PlacefileIcons::Initialize()
    p->uSelectedTimeLocation_ =
       p->shaderProgram_->GetUniformLocation("uSelectedTime");
 
-   glGenVertexArrays(1, &p->vao_);
-   glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl.glGenVertexArrays(1, &p->vao_);
+   gl.glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
-   glBindVertexArray(p->vao_);
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
-
-   // NOLINTBEGIN(modernize-use-nullptr)
-   // NOLINTBEGIN(performance-no-int-to-ptr)
-   // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
+   gl.glBindVertexArray(p->vao_);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aLatLong
-   glVertexAttribPointer(0,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(0);
+   gl.glVertexAttribPointer(0,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(0);
 
    // aXYOffset
-   glVertexAttribPointer(1,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(2 * sizeof(float)));
-   glEnableVertexAttribArray(1);
+   gl.glVertexAttribPointer(1,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(2 * sizeof(float)));
+   gl.glEnableVertexAttribArray(1);
 
    // aModulate
-   glVertexAttribPointer(3,
-                         4,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(4 * sizeof(float)));
-   glEnableVertexAttribArray(3);
+   gl.glVertexAttribPointer(3,
+                            4,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(4 * sizeof(float)));
+   gl.glEnableVertexAttribArray(3);
 
    // aAngle
-   glVertexAttribPointer(4,
-                         1,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(8 * sizeof(float)));
-   glEnableVertexAttribArray(4);
+   gl.glVertexAttribPointer(4,
+                            1,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(8 * sizeof(float)));
+   gl.glEnableVertexAttribArray(4);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aTexCoord
-   glVertexAttribPointer(2,
-                         3,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerTexCoord * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(2);
+   gl.glVertexAttribPointer(2,
+                            3,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerTexCoord * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(2);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[2]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[2]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aThreshold
-   glVertexAttribIPointer(5, //
-                          1,
-                          GL_INT,
-                          0,
-                          static_cast(0));
-   glEnableVertexAttribArray(5);
+   gl.glVertexAttribIPointer(5, //
+                             1,
+                             GL_INT,
+                             0,
+                             static_cast(0));
+   gl.glEnableVertexAttribArray(5);
 
    // aTimeRange
-   glVertexAttribIPointer(6, //
-                          2,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          reinterpret_cast(1 * sizeof(GLint)));
-   glEnableVertexAttribArray(6);
+   gl.glVertexAttribIPointer(6, //
+                             2,
+                             GL_INT,
+                             kIntegersPerVertex_ * sizeof(GLint),
+                             reinterpret_cast(1 * sizeof(GLint)));
+   gl.glEnableVertexAttribArray(6);
 
    // aDisplayed
-   glVertexAttribI1i(7, 1);
-
-   // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
-   // NOLINTEND(performance-no-int-to-ptr)
-   // NOLINTEND(modernize-use-nullptr)
+   gl30.glVertexAttribI1i(7, 1);
 
    p->dirty_ = true;
 }
@@ -272,7 +266,9 @@ void PlacefileIcons::Render(
 
    if (!p->currentIconList_.empty())
    {
-      glBindVertexArray(p->vao_);
+      gl::OpenGLFunctions& gl = p->context_->gl();
+
+      gl.glBindVertexArray(p->vao_);
 
       p->Update(textureAtlasChanged);
       p->shaderProgram_->Use();
@@ -285,38 +281,40 @@ void PlacefileIcons::Render(
          // If thresholding is enabled, set the map distance
          units::length::nautical_miles mapDistance =
             util::maplibre::GetMapDistance(params);
-         glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
+         gl.glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
       }
       else
       {
          // If thresholding is disabled, set the map distance to 0
-         glUniform1f(p->uMapDistanceLocation_, 0.0f);
+         gl.glUniform1f(p->uMapDistanceLocation_, 0.0f);
       }
 
       // Selected time
       std::chrono::system_clock::time_point selectedTime =
          (p->selectedTime_ == std::chrono::system_clock::time_point {}) ?
-            scwx::util::time::now() :
+            std::chrono::system_clock::now() :
             p->selectedTime_;
-      glUniform1i(
+      gl.glUniform1i(
          p->uSelectedTimeLocation_,
          static_cast(std::chrono::duration_cast(
                                selectedTime.time_since_epoch())
                                .count()));
 
       // Interpolate texture coordinates
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
       // Draw icons
-      glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
+      gl.glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
    }
 }
 
 void PlacefileIcons::Deinitialize()
 {
-   glDeleteVertexArrays(1, &p->vao_);
-   glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
+   gl.glDeleteVertexArrays(1, &p->vao_);
+   gl.glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
    std::unique_lock lock {p->iconMutex_};
 
@@ -644,6 +642,8 @@ void PlacefileIcons::Impl::UpdateTextureBuffer()
 
 void PlacefileIcons::Impl::Update(bool textureAtlasChanged)
 {
+   gl::OpenGLFunctions& gl = context_->gl();
+
    // If the texture atlas has changed
    if (dirty_ || textureAtlasChanged)
    {
@@ -657,32 +657,29 @@ void PlacefileIcons::Impl::Update(bool textureAtlasChanged)
       UpdateTextureBuffer();
 
       // Buffer texture data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * textureBuffer_.size()),
-         textureBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * textureBuffer_.size(),
+                      textureBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
    }
 
    // If buffers need updating
    if (dirty_)
    {
       // Buffer vertex data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * currentIconBuffer_.size()),
-         currentIconBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * currentIconBuffer_.size(),
+                      currentIconBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       // Buffer threshold data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[2]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(GLint) * currentIntegerBuffer_.size()),
-         currentIntegerBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[2]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(GLint) * currentIntegerBuffer_.size(),
+                      currentIntegerBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       numVertices_ =
          static_cast(currentIconBuffer_.size() / kPointsPerVertex);
@@ -721,7 +718,7 @@ bool PlacefileIcons::RunMousePicking(
    // If no time has been selected, use the current time
    std::chrono::system_clock::time_point selectedTime =
       (p->selectedTime_ == std::chrono::system_clock::time_point {}) ?
-         scwx::util::time::now() :
+         std::chrono::system_clock::now() :
          p->selectedTime_;
 
    // For each pickable icon
@@ -740,10 +737,8 @@ bool PlacefileIcons::RunMousePicking(
                    units::length::nautical_miles {icon.di_->threshold_}
                       .value())) < 999 &&
 
-                // Map distance is beyond/within the threshold
-                icon.di_->threshold_ < mapDistance &&
-                (icon.di_->threshold_.value() >= 0.0 ||
-                 -(icon.di_->threshold_) > mapDistance)) ||
+                // Map distance is beyond the threshold
+                icon.di_->threshold_ < mapDistance) ||
 
              (
                 // Line has a start time
diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_images.cpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_images.cpp
index b14a2723..aafaef8d 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/placefile_images.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_images.cpp
@@ -2,7 +2,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 #include 
@@ -118,7 +117,7 @@ public:
 };
 
 PlacefileImages::PlacefileImages(const std::shared_ptr& context) :
-    DrawItem(), p(std::make_unique(context))
+    DrawItem(context->gl()), p(std::make_unique(context))
 {
 }
 PlacefileImages::~PlacefileImages() = default;
@@ -140,6 +139,9 @@ void PlacefileImages::set_thresholded(bool thresholded)
 
 void PlacefileImages::Initialize()
 {
+   gl::OpenGLFunctions& gl   = p->context_->gl();
+   auto&                gl30 = p->context_->gl30();
+
    p->shaderProgram_ = p->context_->GetShaderProgram(
       {{GL_VERTEX_SHADER, ":/gl/geo_texture2d.vert"},
        {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"},
@@ -154,81 +156,73 @@ void PlacefileImages::Initialize()
    p->uSelectedTimeLocation_ =
       p->shaderProgram_->GetUniformLocation("uSelectedTime");
 
-   glGenVertexArrays(1, &p->vao_);
-   glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl.glGenVertexArrays(1, &p->vao_);
+   gl.glGenBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
-   glBindVertexArray(p->vao_);
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
-
-   // NOLINTBEGIN(modernize-use-nullptr)
-   // NOLINTBEGIN(performance-no-int-to-ptr)
-   // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
+   gl.glBindVertexArray(p->vao_);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aLatLong
-   glVertexAttribPointer(0,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(0);
+   gl.glVertexAttribPointer(0,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(0);
 
    // aXYOffset
-   glVertexAttribPointer(1,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(2 * sizeof(float)));
-   glEnableVertexAttribArray(1);
+   gl.glVertexAttribPointer(1,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(2 * sizeof(float)));
+   gl.glEnableVertexAttribArray(1);
 
    // aModulate
-   glVertexAttribPointer(3,
-                         4,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(4 * sizeof(float)));
-   glEnableVertexAttribArray(3);
+   gl.glVertexAttribPointer(3,
+                            4,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(4 * sizeof(float)));
+   gl.glEnableVertexAttribArray(3);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aTexCoord
-   glVertexAttribPointer(2,
-                         3,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerTexCoord * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(2);
+   gl.glVertexAttribPointer(2,
+                            3,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerTexCoord * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(2);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[2]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[2]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aThreshold
-   glVertexAttribIPointer(5, //
-                          1,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          static_cast(0));
-   glEnableVertexAttribArray(5);
+   gl.glVertexAttribIPointer(5, //
+                             1,
+                             GL_INT,
+                             kIntegersPerVertex_ * sizeof(GLint),
+                             static_cast(0));
+   gl.glEnableVertexAttribArray(5);
 
    // aTimeRange
-   glVertexAttribIPointer(6, //
-                          2,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          reinterpret_cast(1 * sizeof(GLint)));
-   glEnableVertexAttribArray(6);
+   gl.glVertexAttribIPointer(6, //
+                             2,
+                             GL_INT,
+                             kIntegersPerVertex_ * sizeof(GLint),
+                             reinterpret_cast(1 * sizeof(GLint)));
+   gl.glEnableVertexAttribArray(6);
 
    // aDisplayed
-   glVertexAttribI1i(7, 1);
-
-   // NOLINTEND(cppcoreguidelines-avoid-magic-numbers)
-   // NOLINTEND(performance-no-int-to-ptr)
-   // NOLINTEND(modernize-use-nullptr)
+   gl30.glVertexAttribI1i(7, 1);
 
    p->dirty_ = true;
 }
@@ -241,7 +235,9 @@ void PlacefileImages::Render(
 
    if (!p->currentImageList_.empty())
    {
-      glBindVertexArray(p->vao_);
+      gl::OpenGLFunctions& gl = p->context_->gl();
+
+      gl.glBindVertexArray(p->vao_);
 
       p->Update(textureAtlasChanged);
       p->shaderProgram_->Use();
@@ -254,38 +250,40 @@ void PlacefileImages::Render(
          // If thresholding is enabled, set the map distance
          units::length::nautical_miles mapDistance =
             util::maplibre::GetMapDistance(params);
-         glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
+         gl.glUniform1f(p->uMapDistanceLocation_, mapDistance.value());
       }
       else
       {
          // If thresholding is disabled, set the map distance to 0
-         glUniform1f(p->uMapDistanceLocation_, 0.0f);
+         gl.glUniform1f(p->uMapDistanceLocation_, 0.0f);
       }
 
       // Selected time
       std::chrono::system_clock::time_point selectedTime =
          (p->selectedTime_ == std::chrono::system_clock::time_point {}) ?
-            scwx::util::time::now() :
+            std::chrono::system_clock::now() :
             p->selectedTime_;
-      glUniform1i(
+      gl.glUniform1i(
          p->uSelectedTimeLocation_,
          static_cast(std::chrono::duration_cast(
                                selectedTime.time_since_epoch())
                                .count()));
 
       // Interpolate texture coordinates
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-      glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+      gl.glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
       // Draw images
-      glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
+      gl.glDrawArrays(GL_TRIANGLES, 0, p->numVertices_);
    }
 }
 
 void PlacefileImages::Deinitialize()
 {
-   glDeleteVertexArrays(1, &p->vao_);
-   glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
+   gl::OpenGLFunctions& gl = p->context_->gl();
+
+   gl.glDeleteVertexArrays(1, &p->vao_);
+   gl.glDeleteBuffers(static_cast(p->vbo_.size()), p->vbo_.data());
 
    std::unique_lock lock {p->imageMutex_};
 
@@ -441,6 +439,8 @@ void PlacefileImages::Impl::UpdateTextureBuffer()
 
 void PlacefileImages::Impl::Update(bool textureAtlasChanged)
 {
+   gl::OpenGLFunctions& gl = context_->gl();
+
    // If the texture atlas has changed
    if (dirty_ || textureAtlasChanged)
    {
@@ -454,32 +454,29 @@ void PlacefileImages::Impl::Update(bool textureAtlasChanged)
       UpdateTextureBuffer();
 
       // Buffer texture data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * textureBuffer_.size()),
-         textureBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[1]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * textureBuffer_.size(),
+                      textureBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
    }
 
    // If buffers need updating
    if (dirty_)
    {
       // Buffer vertex data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(float) * currentImageBuffer_.size()),
-         currentImageBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[0]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(float) * currentImageBuffer_.size(),
+                      currentImageBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       // Buffer threshold data
-      glBindBuffer(GL_ARRAY_BUFFER, vbo_[2]);
-      glBufferData(
-         GL_ARRAY_BUFFER,
-         static_cast(sizeof(GLint) * currentIntegerBuffer_.size()),
-         currentIntegerBuffer_.data(),
-         GL_DYNAMIC_DRAW);
+      gl.glBindBuffer(GL_ARRAY_BUFFER, vbo_[2]);
+      gl.glBufferData(GL_ARRAY_BUFFER,
+                      sizeof(GLint) * currentIntegerBuffer_.size(),
+                      currentIntegerBuffer_.data(),
+                      GL_DYNAMIC_DRAW);
 
       numVertices_ =
          static_cast(currentImageBuffer_.size() / kPointsPerVertex);
diff --git a/scwx-qt/source/scwx/qt/gl/draw/placefile_lines.cpp b/scwx-qt/source/scwx/qt/gl/draw/placefile_lines.cpp
index b908389c..8fdce9f1 100644
--- a/scwx-qt/source/scwx/qt/gl/draw/placefile_lines.cpp
+++ b/scwx-qt/source/scwx/qt/gl/draw/placefile_lines.cpp
@@ -3,7 +3,6 @@
 #include 
 #include 
 #include 
-#include 
 
 #include 
 
@@ -19,9 +18,13 @@ namespace draw
 static const std::string logPrefix_ = "scwx::qt::gl::draw::placefile_lines";
 static const auto        logger_    = scwx::util::Logger::Create(logPrefix_);
 
+static constexpr std::size_t kNumRectangles        = 1;
+static constexpr std::size_t kNumTriangles         = kNumRectangles * 2;
 static constexpr std::size_t kVerticesPerTriangle  = 3;
 static constexpr std::size_t kVerticesPerRectangle = kVerticesPerTriangle * 2;
 static constexpr std::size_t kPointsPerVertex      = 9;
+static constexpr std::size_t kBufferLength =
+   kNumTriangles * kVerticesPerTriangle * kPointsPerVertex;
 
 // Threshold, start time, end time
 static constexpr std::size_t kIntegersPerVertex_ = 3;
@@ -107,7 +110,7 @@ public:
 };
 
 PlacefileLines::PlacefileLines(const std::shared_ptr& context) :
-    DrawItem(), p(std::make_unique(context))
+    DrawItem(context->gl()), p(std::make_unique(context))
 {
 }
 PlacefileLines::~PlacefileLines() = default;
@@ -128,6 +131,9 @@ void PlacefileLines::set_thresholded(bool thresholded)
 
 void PlacefileLines::Initialize()
 {
+   gl::OpenGLFunctions& gl   = p->context_->gl();
+   auto&                gl30 = p->context_->gl30();
+
    p->shaderProgram_ = p->context_->GetShaderProgram(
       {{GL_VERTEX_SHADER, ":/gl/geo_texture2d.vert"},
        {GL_GEOMETRY_SHADER, ":/gl/threshold.geom"},
@@ -142,78 +148,70 @@ void PlacefileLines::Initialize()
    p->uSelectedTimeLocation_ =
       p->shaderProgram_->GetUniformLocation("uSelectedTime");
 
-   glGenVertexArrays(1, &p->vao_);
-   glGenBuffers(2, p->vbo_.data());
+   gl.glGenVertexArrays(1, &p->vao_);
+   gl.glGenBuffers(2, p->vbo_.data());
 
-   glBindVertexArray(p->vao_);
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
-
-   // NOLINTBEGIN(modernize-use-nullptr)
-   // NOLINTBEGIN(performance-no-int-to-ptr)
-   // NOLINTBEGIN(cppcoreguidelines-avoid-magic-numbers)
+   gl.glBindVertexArray(p->vao_);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[0]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aLatLong
-   glVertexAttribPointer(0,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         static_cast(0));
-   glEnableVertexAttribArray(0);
+   gl.glVertexAttribPointer(0,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            static_cast(0));
+   gl.glEnableVertexAttribArray(0);
 
    // aXYOffset
-   glVertexAttribPointer(1,
-                         2,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(2 * sizeof(float)));
-   glEnableVertexAttribArray(1);
+   gl.glVertexAttribPointer(1,
+                            2,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(2 * sizeof(float)));
+   gl.glEnableVertexAttribArray(1);
 
    // aModulate
-   glVertexAttribPointer(3,
-                         4,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(4 * sizeof(float)));
-   glEnableVertexAttribArray(3);
+   gl.glVertexAttribPointer(3,
+                            4,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(4 * sizeof(float)));
+   gl.glEnableVertexAttribArray(3);
 
    // aAngle
-   glVertexAttribPointer(4,
-                         1,
-                         GL_FLOAT,
-                         GL_FALSE,
-                         kPointsPerVertex * sizeof(float),
-                         reinterpret_cast(8 * sizeof(float)));
-   glEnableVertexAttribArray(4);
+   gl.glVertexAttribPointer(4,
+                            1,
+                            GL_FLOAT,
+                            GL_FALSE,
+                            kPointsPerVertex * sizeof(float),
+                            reinterpret_cast(8 * sizeof(float)));
+   gl.glEnableVertexAttribArray(4);
 
-   glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
-   glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
+   gl.glBindBuffer(GL_ARRAY_BUFFER, p->vbo_[1]);
+   gl.glBufferData(GL_ARRAY_BUFFER, 0u, nullptr, GL_DYNAMIC_DRAW);
 
    // aThreshold
-   glVertexAttribIPointer(5, //
-                          1,
-                          GL_INT,
-                          kIntegersPerVertex_ * sizeof(GLint),
-                          static_cast