diff --git a/javascript/nextjs/tables/.gitignore b/javascript/nextjs/tables/.gitignore
new file mode 100644
index 0000000..5ef6a52
--- /dev/null
+++ b/javascript/nextjs/tables/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/javascript/nextjs/tables/README.md b/javascript/nextjs/tables/README.md
new file mode 100644
index 0000000..e215bc4
--- /dev/null
+++ b/javascript/nextjs/tables/README.md
@@ -0,0 +1,36 @@
+This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
+
+## Getting Started
+
+First, run the development server:
+
+```bash
+npm run dev
+# or
+yarn dev
+# or
+pnpm dev
+# or
+bun dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
+
+## Learn More
+
+To learn more about Next.js, take a look at the following resources:
+
+- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
+- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
+
+You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
+
+## Deploy on Vercel
+
+The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
+
+Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
diff --git a/javascript/nextjs/tables/app/components/BasicTable.tsx b/javascript/nextjs/tables/app/components/BasicTable.tsx
new file mode 100644
index 0000000..518e98a
--- /dev/null
+++ b/javascript/nextjs/tables/app/components/BasicTable.tsx
@@ -0,0 +1,121 @@
+'use client'
+import { useState } from "react";
+import { ChevronDown, ChevronUp } from "lucide-react";
+
+const DESCENDING = "desc"
+const ASCENDING = "asc"
+
+const getTableHeader = (column, sortField, sortDirection) => {
+ let columnValue = column.name
+ // TODO: If any columns can't be sorted the cursor should be cursor-not-allowed
+ let defaultClassName = "flex justify-center space-x-2 cursor-pointer"
+ if(column.key == sortField && sortDirection == DESCENDING){
+ return (
+
+ {column.name}
+
+ )
+ }else if(column.key == sortField && sortDirection == ASCENDING){
+ return (
+
+ )
+ }
+ return (
+
+ {column.name}
+
+ )
+}
+
+const BasicTable = ({columns, data}) => {
+ // console.log(`Data = ${JSON.stringify(data)}`)
+ // const rows = data.forEach((entry, idx) => {
+ // return
+ // })
+ // const [cursorColumn, setCursorColumn] = useState("")
+ const [tableData, setTableData] = useState(data)
+ const [sortDirection, setSortDirection] = useState("")
+ const [sortField, setSortField] = useState("")
+
+ const handleColumnClick = (column, event) => {
+ console.log(`click tracked on ${sortField} direction ${sortDirection}`)
+ setSortField(column.key)
+ let sortDirUpdate = (sortDirection == "" || sortDirection == DESCENDING) ? ASCENDING : DESCENDING;
+ console.log(sortDirUpdate)
+ setSortDirection(sortDirUpdate)
+ // Call function to sort data.
+ const sortedData = [...data].sort((a, b) => {
+ let a_value = a[column.key].toString()
+ let b_value = b[column.key].toString()
+ if(sortDirection == ASCENDING)
+ return a_value.localeCompare(b_value)
+ else
+ return b_value.localeCompare(a_value)
+ })
+
+ console.log(`Sorted Data by column ${column.key} \n ${JSON.stringify(data)}`)
+ // Update data via setTableData
+ setTableData(sortedData)
+ }
+
+ const handleMouseEnter = (column, event) => {
+ console.log(`Mouse entered ${column.name}`)
+ }
+
+ const handleMouseLeave = (column, event) => {
+ console.log(`Mouse left ${column.name}`)
+ }
+
+ return (
+
+
+ Very Simple Basic Table with Row Coloring
+
+
+
+
+ {columns.map((column) => (
+ handleColumnClick(column, e)}
+ onMouseEnter={e => handleMouseEnter(column, e)}
+ onMouseLeave={e => handleMouseLeave(column, e)}
+ >
+ {getTableHeader(column, sortField, sortDirection)}
+
+ ))}
+
+
+
+ {tableData.map((entry, idx) => {
+ return (
+
+ {columns.map((column) => {
+ // console.log(`Making it into TR ${entry}`)
+ return (
+
+ {entry[column.key]}
+
+ )
+ })}
+
+ )
+ })}
+
+
+
+ )
+}
+
+export default BasicTable
\ No newline at end of file
diff --git a/javascript/nextjs/tables/app/components/FixedColumnTable.tsx b/javascript/nextjs/tables/app/components/FixedColumnTable.tsx
new file mode 100644
index 0000000..151219c
--- /dev/null
+++ b/javascript/nextjs/tables/app/components/FixedColumnTable.tsx
@@ -0,0 +1,16 @@
+'use client'
+import { useState } from "react";
+import { ChevronDown, ChevronUp} from "lucide-react";
+
+const FixedColumnTable = ({columns, data}) => {
+
+ return (
+
+
+
+ )
+}
+
+export default FixedColumnTable
\ No newline at end of file
diff --git a/javascript/nextjs/tables/app/data/people.json b/javascript/nextjs/tables/app/data/people.json
new file mode 100644
index 0000000..ca02110
--- /dev/null
+++ b/javascript/nextjs/tables/app/data/people.json
@@ -0,0 +1,50 @@
+[
+ {
+ "name": "Michael Scott",
+ "position": "Regional Manager",
+ "episodes": 2000,
+ "company": "Dunder Mifflin",
+ "email": "michael.scott@dundermifflin.com",
+ "phone_number": "800-123-4567"
+ },
+ {
+ "name": "Dwight Schrute",
+ "position": "Assitant to Regional Manager",
+ "company": "Dunder Mifflin",
+ "episodes": 1000,
+ "email": "dwight.schrute@dundermifflin.com",
+ "phone_number": "800-456-8901"
+ },
+ {
+ "name": "Jim Halpert",
+ "position": "Salesman",
+ "company": "Dunder Mifflin",
+ "episodes": 2000,
+ "email": "jim.halpert@dundermifflin.com",
+ "phone_number": "800-456-8901"
+ },
+ {
+ "name": "Pam Halpert",
+ "position": "Receptionist/Saleswoman",
+ "company": "Dunder Mifflin",
+ "episodes": 2000,
+ "email": "pam.halpert@dundermifflin.com",
+ "phone_number": "800-456-8902"
+ },
+ {
+ "name": "Kevin Malone",
+ "position": "Accountant",
+ "company": "Dunder Mifflin",
+ "episodes": 2000,
+ "email": "kevin.malone@dundermifflin.com",
+ "phone_number": "800-124-5678"
+ },
+ {
+ "name": "Oscar Martinez",
+ "position": "Accountant",
+ "company": "Dunder Mifflin",
+ "episodes": 2000,
+ "email": "oscar.martinez@dundermifflin.com",
+ "phone_number": "800-123-5678"
+ }
+]
\ No newline at end of file
diff --git a/javascript/nextjs/tables/app/favicon.ico b/javascript/nextjs/tables/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/javascript/nextjs/tables/app/favicon.ico differ
diff --git a/javascript/nextjs/tables/app/globals.css b/javascript/nextjs/tables/app/globals.css
new file mode 100644
index 0000000..a2dc41e
--- /dev/null
+++ b/javascript/nextjs/tables/app/globals.css
@@ -0,0 +1,26 @@
+@import "tailwindcss";
+
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ background: var(--background);
+ color: var(--foreground);
+ font-family: Arial, Helvetica, sans-serif;
+}
diff --git a/javascript/nextjs/tables/app/layout.tsx b/javascript/nextjs/tables/app/layout.tsx
new file mode 100644
index 0000000..f7fa87e
--- /dev/null
+++ b/javascript/nextjs/tables/app/layout.tsx
@@ -0,0 +1,34 @@
+import type { Metadata } from "next";
+import { Geist, Geist_Mono } from "next/font/google";
+import "./globals.css";
+
+const geistSans = Geist({
+ variable: "--font-geist-sans",
+ subsets: ["latin"],
+});
+
+const geistMono = Geist_Mono({
+ variable: "--font-geist-mono",
+ subsets: ["latin"],
+});
+
+export const metadata: Metadata = {
+ title: "Create Next App",
+ description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/javascript/nextjs/tables/app/page.tsx b/javascript/nextjs/tables/app/page.tsx
new file mode 100644
index 0000000..9de0c52
--- /dev/null
+++ b/javascript/nextjs/tables/app/page.tsx
@@ -0,0 +1,86 @@
+import Image from "next/image";
+import BasicTable from "@/app/components/BasicTable";
+import data from "./data/people.json"
+import FixedColumnTable from "@/app/components/FixedColumnTable";
+
+
+export default function Home() {
+ const columns = [
+ {key: "name", name: "Name"},
+ {key: "position", name: "Position"},
+ {key: "episodes", name: "Episodes"},
+ {key: "company", name: "Company"},
+ {key: "email", name: "Email"},
+ {key: "phone_number", name: "Phone Number"}
+ ]
+
+ return (
+
+ //
+ //
+ //
+ //
+ //
+ // To get started, edit the page.tsx file.
+ //
+ //
+ // Looking for a starting point or more instructions? Head over to{" "}
+ //
+ // Templates
+ // {" "}
+ // or the{" "}
+ //
+ // Learning
+ // {" "}
+ // center.
+ //
+ //
+ //
+ //
+ //
+ );
+}
diff --git a/javascript/nextjs/tables/eslint.config.mjs b/javascript/nextjs/tables/eslint.config.mjs
new file mode 100644
index 0000000..05e726d
--- /dev/null
+++ b/javascript/nextjs/tables/eslint.config.mjs
@@ -0,0 +1,18 @@
+import { defineConfig, globalIgnores } from "eslint/config";
+import nextVitals from "eslint-config-next/core-web-vitals";
+import nextTs from "eslint-config-next/typescript";
+
+const eslintConfig = defineConfig([
+ ...nextVitals,
+ ...nextTs,
+ // Override default ignores of eslint-config-next.
+ globalIgnores([
+ // Default ignores of eslint-config-next:
+ ".next/**",
+ "out/**",
+ "build/**",
+ "next-env.d.ts",
+ ]),
+]);
+
+export default eslintConfig;
diff --git a/javascript/nextjs/tables/next.config.ts b/javascript/nextjs/tables/next.config.ts
new file mode 100644
index 0000000..e9ffa30
--- /dev/null
+++ b/javascript/nextjs/tables/next.config.ts
@@ -0,0 +1,7 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ /* config options here */
+};
+
+export default nextConfig;
diff --git a/javascript/nextjs/tables/package.json b/javascript/nextjs/tables/package.json
new file mode 100644
index 0000000..93651c8
--- /dev/null
+++ b/javascript/nextjs/tables/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "tables",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint"
+ },
+ "dependencies": {
+ "lucide-react": "^0.553.0",
+ "next": "16.0.1",
+ "react": "19.2.0",
+ "react-dom": "19.2.0"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "16.0.1",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ }
+}
diff --git a/javascript/nextjs/tables/postcss.config.mjs b/javascript/nextjs/tables/postcss.config.mjs
new file mode 100644
index 0000000..61e3684
--- /dev/null
+++ b/javascript/nextjs/tables/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+};
+
+export default config;
diff --git a/javascript/nextjs/tables/public/file.svg b/javascript/nextjs/tables/public/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/javascript/nextjs/tables/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/javascript/nextjs/tables/public/globe.svg b/javascript/nextjs/tables/public/globe.svg
new file mode 100644
index 0000000..567f17b
--- /dev/null
+++ b/javascript/nextjs/tables/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/javascript/nextjs/tables/public/next.svg b/javascript/nextjs/tables/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/javascript/nextjs/tables/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/javascript/nextjs/tables/public/vercel.svg b/javascript/nextjs/tables/public/vercel.svg
new file mode 100644
index 0000000..7705396
--- /dev/null
+++ b/javascript/nextjs/tables/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/javascript/nextjs/tables/public/window.svg b/javascript/nextjs/tables/public/window.svg
new file mode 100644
index 0000000..b2b2a44
--- /dev/null
+++ b/javascript/nextjs/tables/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/javascript/nextjs/tables/tsconfig.json b/javascript/nextjs/tables/tsconfig.json
new file mode 100644
index 0000000..3a13f90
--- /dev/null
+++ b/javascript/nextjs/tables/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts",
+ "**/*.mts"
+ ],
+ "exclude": ["node_modules"]
+}
diff --git a/python/python-examples/test/test_haversine_distance.py b/python/python-examples/test/test_haversine_distance.py
index d5f681c..5a29ceb 100644
--- a/python/python-examples/test/test_haversine_distance.py
+++ b/python/python-examples/test/test_haversine_distance.py
@@ -2,6 +2,7 @@
Test for haversine distance.
"""
import logging as log
+import numpy as np
from math import radians
import pytest
from sklearn.metrics.pairwise import haversine_distances
@@ -12,6 +13,11 @@
pytest.param(
[-34.83333, -58.5166646],
[49.0083899664, 2.53844117956]
+ ),
+ # Google location vs USDA Location
+ pytest.param(
+ [39.0168311, -76.92883499999999],
+ [39.01644, -76.928925]
)
])
def test_calculate_haversince_distance(lat_long_origin:list, lat_long_destination:list):
@@ -28,4 +34,43 @@ def test_calculate_haversince_distance(lat_long_origin:list, lat_long_destinatio
sklearn_distance = km_distance_matrix_result[0][1]
log.info(f"Raw Calc={km_haversine_distance}, Sklearn Calc={sklearn_distance}")
# Rounding both to 8 digits of accuracy...
- assert km_haversine_distance == round(km_distance_matrix_result[0][1], 8)
\ No newline at end of file
+ assert km_haversine_distance == round(km_distance_matrix_result[0][1], 8)
+
+
+def test_shortest_distance():
+ # Lincoln Memorial Latlong
+ lincoln_memorial = [38.889248, -77.050636]
+
+ # Ducinni's off U St
+ ducinnis_pizza = [38.91706701509112, -77.04118715785616]
+
+ # Admo Jumbo Slice
+ jumbo_slice_pizza = [38.92105748065034, -77.04170211776945]
+
+ # Manny Olgas Near U
+ manny_olgas = [38.91543818061708, -77.03174726038766]
+
+ # Based off Haversine which is closest to Lincoln Memorial
+ dataset = [
+ {'name': 'Lincoln Memorial', 'geocode': lincoln_memorial},
+ {'name': "Ducinni's Pizza", 'geocode': ducinnis_pizza},
+ {'name': 'Jumbo Slice Pizza', 'geocode': jumbo_slice_pizza},
+ {'name': "Manny Olga's", 'geocode': manny_olgas}
+ ]
+
+ # Put all these in radians
+ radian_distances = list(map(lambda x: [radians(_) for _ in x['geocode']], dataset))
+
+ # Calculate the Haversine Distances
+ distance_matrix_result = haversine_distances(radian_distances)
+
+ # Pull out the first Entry in the 2D array
+ km_distance_matrix_result = distance_matrix_result * RADIUS_OF_EARTH / 1000
+ log.info(f"\nDistance Matrix in Kilometers:\n {km_distance_matrix_result}")
+
+ # Get the index of the minimum distance for the lincoln memorial
+ np_array = np.array(km_distance_matrix_result[0][1:])
+ idx_min = np.argmin(np_array)
+ # Closest Should be Ducinnis
+ assert idx_min + 1 == 1
+ log.info(f"\nClosest Jumbo Slice spot to Lincoln Memorial by Haversine Distance is {dataset[idx_min + 1]['name']}")