diff --git a/package-lock.json b/package-lock.json index 73a7ba1..c3ca9bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ }, "devDependencies": { "@eslint/js": "^9.21.0", + "@types/ol": "^6.5.3", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", @@ -1219,6 +1220,13 @@ "win32" ] }, + "node_modules/@types/arcgis-rest-api": { + "version": "10.4.8", + "resolved": "https://registry.npmjs.org/@types/arcgis-rest-api/-/arcgis-rest-api-10.4.8.tgz", + "integrity": "sha512-MapiW0nP/5X0BUHmtRNEjbK0xrxS6n0syZWQk25H4ObFFwuK1+pGl7LQfOLgpsVM849WQ1b2sKIvWqEAvPwzCA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1270,12 +1278,32 @@ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "devOptional": true }, + "node_modules/@types/ol": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@types/ol/-/ol-6.5.3.tgz", + "integrity": "sha512-C0Sm5dytTfRxztYFMuWq0IBJYfMffPF4GzXI/s4ZyJlNZ8AAdS+9fW0iGOyYkTb3ZQmfYWYdAzMqwGXfHVw+3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/arcgis-rest-api": "*", + "@types/geojson": "*", + "@types/rbush": "*", + "@types/topojson-specification": "*" + } + }, "node_modules/@types/rbush": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/rbush/-/rbush-4.0.0.tgz", @@ -1299,6 +1327,16 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/topojson-specification": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/topojson-specification/-/topojson-specification-1.0.5.tgz", + "integrity": "sha512-C7KvcQh+C2nr6Y2Ub4YfgvWvWCgP2nOQMtfhlnwsRL4pYmmwzBS7HclGiS87eQfDOU/DLQpX6GEscviaz4yLIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.29.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", @@ -2512,6 +2550,7 @@ "version": "10.5.0", "resolved": "https://registry.npmjs.org/ol/-/ol-10.5.0.tgz", "integrity": "sha512-nHFx8gkGmvYImsa7iKkwUnZidd5gn1XbMZd9GNOorvm9orjW9gQvT3Naw/MjIasVJ3cB9EJUdCGR2EFAulMHsQ==", + "license": "BSD-2-Clause", "dependencies": { "@types/rbush": "4.0.0", "earcut": "^3.0.0", @@ -2528,6 +2567,7 @@ "version": "4.0.31", "resolved": "https://registry.npmjs.org/ol-ext/-/ol-ext-4.0.31.tgz", "integrity": "sha512-byh26JcXZggkLUY79+c29cxf2ovkkwz5niWJzOIN+dLPDJ+ogSn0y4Wl8fIS9O2JtQff5/cInHRImgZBvl+N1g==", + "license": "BSD-3-Clause", "peerDependencies": { "ol": ">= 5.3.0" } diff --git a/package.json b/package.json index af27824..5c2aab1 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@eslint/js": "^9.21.0", + "@types/ol": "^6.5.3", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", "@vitejs/plugin-react": "^4.3.4", diff --git a/src/components/LegiScanAPI.tsx b/src/components/LegiScanAPI.tsx index 1798edf..fd6e18f 100644 --- a/src/components/LegiScanAPI.tsx +++ b/src/components/LegiScanAPI.tsx @@ -1,11 +1,11 @@ -//import { useState, useEffect } from 'react' +import { useState, useEffect } from 'react' async function getBills(data) { const bill_ids: number[] = []; const bill_titles: string[] = []; var results: any = data["searchresult"]["results"]; for(var i = 0; i < data["searchresult"]["summary"]["count"]; i++){ - var bill_id: integer = results[i]["bill_id"]; + var bill_id: number = results[i]["bill_id"]; bill_ids.push(bill_id); } for(var i = 0; i < bill_ids.length; i++){ diff --git a/src/components/OpenLayerEsri.tsx b/src/components/OpenLayerEsri.tsx index 3a8dec6..bc26067 100644 --- a/src/components/OpenLayerEsri.tsx +++ b/src/components/OpenLayerEsri.tsx @@ -1,17 +1,17 @@ -import {useRef} from 'react'; +import { useRef } from 'react'; import Map from 'ol/Map'; import View from 'ol/View'; import EsriJSON from 'ol/format/EsriJSON'; import TileLayer from 'ol/layer/Tile'; import VectorLayer from 'ol/layer/Vector'; -import {tile as tileStrategy} from 'ol/loadingstrategy'; -import {fromLonLat} from 'ol/proj'; +import { tile as tileStrategy } from 'ol/loadingstrategy'; +import { fromLonLat } from 'ol/proj'; import ImageTile from 'ol/source/ImageTile'; import VectorSource from 'ol/source/Vector'; import Fill from 'ol/style/Fill'; import Stroke from 'ol/style/Stroke'; import Style from 'ol/style/Style'; -import {createXYZ} from 'ol/tilegrid'; +import { createXYZ } from 'ol/tilegrid'; const serviceUrl = 'https://services-eu1.arcgis.com/NPIbx47lsIiu2pqz/ArcGIS/rest/services/' + @@ -44,23 +44,23 @@ const style = new Style({ const OpenLayerEsri = () => { -const mapDivRef = useRef(null); -const vectorSource = new VectorSource({ - format: new EsriJSON(), - url: function (extent, projection) { - // ArcGIS Server only wants the numeric portion of the projection ID. - const srid = projection - .getCode() - .split(/:(?=\d+$)/) - .pop(); + const mapDivRef = useRef(null); + const vectorSource = new VectorSource({ + format: new EsriJSON(), + url: function (extent, projection) { + // ArcGIS Server only wants the numeric portion of the projection ID. + const srid = projection + .getCode() + .split(/:(?=\d+$)/) + .pop(); - const url = - serviceUrl + - layer + - '/query/?f=json&' + - 'returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometry=' + - encodeURIComponent( - '{"xmin":' + + const url = + serviceUrl + + layer + + '/query/?f=json&' + + 'returnGeometry=true&spatialRel=esriSpatialRelIntersects&geometry=' + + encodeURIComponent( + '{"xmin":' + extent[0] + ',"ymin":' + extent[1] + @@ -71,62 +71,62 @@ const vectorSource = new VectorSource({ ',"spatialReference":{"wkid":' + srid + '}}', - ) + - '&geometryType=esriGeometryEnvelope&inSR=' + - srid + - '&outFields=*' + - '&outSR=' + - srid; + ) + + '&geometryType=esriGeometryEnvelope&inSR=' + + srid + + '&outFields=*' + + '&outSR=' + + srid; - return url; - }, - strategy: tileStrategy( - createXYZ({ - tileSize: 512, - }), - ), - attributions: - 'University of Leicester (commissioned by the ' + - 'National Trust)', -}); - -const vector = new VectorLayer({ - source: vectorSource, - style: function (feature) { - const classify = feature.get('LU_2014'); - const color = fillColors[classify] || [0, 0, 0, 0]; - style.getFill().setColor(color); - return style; - }, - opacity: 0.7, -}); - -const raster = new TileLayer({ - source: new ImageTile({ + return url; + }, + strategy: tileStrategy( + createXYZ({ + tileSize: 512, + }), + ), attributions: - 'Tiles © ArcGIS', - url: - 'https://server.arcgisonline.com/ArcGIS/rest/services/' + - 'World_Topo_Map/MapServer/tile/{z}/{y}/{x}', - }), -}); + 'University of Leicester (commissioned by the ' + + 'National Trust)', + }); -const map = new Map({ - layers: [raster,vector], - target: mapDivRef.current as HTMLDivElement, - view: new View({ - center: fromLonLat([1.72, 52.4]), - zoom: 14, - }), -}); + const vector = new VectorLayer({ + source: vectorSource, + style: function (feature) { + const classify = feature.get('LU_2014'); + const color = fillColors[classify] || [0, 0, 0, 0]; + style.getFill().setColor(color); + return style; + }, + opacity: 0.7, + }); + + const raster = new TileLayer({ + source: new ImageTile({ + attributions: + 'Tiles © ArcGIS', + url: + 'https://server.arcgisonline.com/ArcGIS/rest/services/' + + 'World_Topo_Map/MapServer/tile/{z}/{y}/{x}', + }), + }); + + const map = new Map({ + layers: [raster, vector], + target: mapDivRef.current as HTMLDivElement, + view: new View({ + center: fromLonLat([1.72, 52.4]), + zoom: 14, + }), + }); - return ( - <> -
- - ); + return ( + <> +
+ + ); } export default OpenLayerEsri diff --git a/src/components/OpenLayerMap.tsx b/src/components/OpenLayerMap.tsx index d367e6d..17ead8c 100644 --- a/src/components/OpenLayerMap.tsx +++ b/src/components/OpenLayerMap.tsx @@ -1,259 +1,183 @@ import { useEffect, useRef, useState } from 'react'; import { Map, View } from 'ol'; - import 'ol/ol.css'; import TileLayer from 'ol/layer/Tile'; -import Overlay from 'ol/Overlay' -//import Popup from 'ol/Popup' -//import {tile as tileStrategy} from 'ol/loadingstrategy'; -//import {createXYZ} from 'ol/tilegrid'; -//import {Search} from 'ol-ext/control/SearchFeature'; import { OSM } from 'ol/source'; -import {fromLonLat} from 'ol/proj'; -//import {Select} from 'ol/interaction/Select'; +import { fromLonLat } from 'ol/proj'; import { Coordinate } from 'ol/coordinate'; -import VectorLayer from "ol/layer/Vector" -import VectorSource from "ol/source/Vector" -import GeoJSON from "ol/format/GeoJSON" -//import EsriJSON from "ol/format/EsriJSON" -import {Style, Icon, Fill, Circle, Stroke} from "ol/style" +import VectorLayer from 'ol/layer/Vector'; +import VectorSource from 'ol/source/Vector'; +import GeoJSON from 'ol/format/GeoJSON'; +import Style from 'ol/style/Style'; +import Stroke from 'ol/style/Stroke'; +import Fill from 'ol/style/Fill'; const OpenLayersMap = () => { - const [term, setTerm] = useState({}); + // const [term, setTerm] = useState({}); + const [term, setTerm] = useState(''); const [address, setAddress] = useState(); + + //Submit Button Logic + const submitForm = async (event: React.FormEvent) => { // Preventing the page from reloading event.preventDefault(); - //fetch(`https://nominatim.openstreetmap.org/search?q=${term}&format=json&polygon=1&addressdetails=1`) - // .then(response => response.json()) - //.then(response => console.log(response)) - // .then(({data: address}) => {setAddress(address)}) - //console.log(address) const response = await fetch(`https://nominatim.openstreetmap.org/search?q=${term}&format=json&polygon=1&addressdetails=1`) const json = await response.json(); console.log(json); setAddress(json); console.log(address); - - // Do something alert(term); } - const [data,setData] = useState({}); - const mapDivRef = useRef(null); - const popupRef = useRef(null); - const [clickedCoordinate, setClickedCoordinate] = useState(); - - const url_1: any = "https://geo.sandag.org/server/rest/services/Hosted/Address_Points/FeatureServer/0/query?where=1%3D1&outFields=*&returnGeometry=true&resultType=standard&f=geojson"; - const url_2: any = "https://geo.sandag.org/server/rest/services/Hosted/Parcels/FeatureServer/0/query?where=1%3D1&outFields=*&returnGeometry=true&resultType=none&f=geojson"; - const url_3: any = "https://geo.sandag.org/server/rest/services/Hosted/Zoning_Base_SD/FeatureServer/0/query?where=1%3D1&outFields=*&returnGeometry=true&resultType=standard&f=geojson"; - const url_4: any = "https://geo.sandag.org/server/rest/services/Hosted/Water_Main_SD/FeatureServer/0/query?where=1%3D1&outFields=*&returnGeometry=true&resultType=standard&f=geojson"; + //Submit Button Logic -const vectorLayer: any = new VectorLayer({ - source: new VectorSource({ - format: new GeoJSON(), - url: url_2, -style: new Style({ - fill: new Fill({ - color: 'red', - }), - stroke: new Stroke({ - color: 'black', - width: 2.25 - }), - }), - }), + //const [data, setData] = useState({}); + const mapDivRef = useRef(null); + const [clickedCoordinate, setClickedCoordinate] = useState(); + const mapRef = useRef(null); -}); -const vectorsource_offset: any = new VectorSource(); -const vectorsource_offset_1: any = new VectorSource(); -const format: any = new GeoJSON(); - //vectorsource_offset.addFeature(format.readFeatures(data)) + useEffect(() => { -const vectorLayer2: any = new VectorLayer({ - source: vectorsource_offset, - -}); -const vectorLayer1: any = new VectorLayer({ - source: new VectorSource({ - format: new GeoJSON(), - url: url_1, -attributions: ['Los Angeles GeoHub |'] - }), - -}); - -const vectorLayer_given: any = new VectorLayer(); -const vectorLayer_parcel: any = new VectorLayer(); - - // default view - - const vecLayerCreation = async (url,length,map,vectorLayer) => { - -const vectorsource_layer: any = new VectorSource(); -const format: any = new GeoJSON(); -const urls: string[] = []; -for(var i = 0; i < length; i = i + 2000){ - urls.push(url + `&resultOffset=${i}`); -} -console.log(urls) - urls.forEach((url_each,index) => { - fetch(url_each) - .then(response => response.json()) - .then(response => vectorsource_layer.addFeatures(format.readFeatures(response, { - dataProjection: 'EPSG:4326', - featureProjection: map.getView().getProjection(), - }))) - .catch(error => console.error(error)); - - }) - //vectorsource_offset.addFeature(format.readFeatures(data)) -vectorLayer.setSource(vectorsource_layer) -console.log(vectorLayer) - - - } - useEffect(() => { - -var ar = [-117.9112790,34.0296349] + //Initial Location + var ar = [-117.9112790, 34.0296349] var proj_lat_long = fromLonLat(ar); -const overlay = new Overlay({ - element: popupRef.current as HTMLDivElement, - autoPan: { - animation: { - duration: 250, - }, - }, - }) - const map = new Map({ - target: mapDivRef.current as HTMLDivElement, - layers: [ - new TileLayer({ - source: new OSM(), - }), - ], -view: new View({ - center: [-13042228, 3857849], - //center: proj_lat_long, - zoom: 12, - }), - overlays: [overlay], - }); - -//vecLayerCreation(url_4,3718,map,vectorLayer_given) -//vecLayerCreation(url_2,1088001,map,vectorLayer_parcel) - - //map.addLayer(vectorLayer_given) - - //const true_we = map.setLayers([vectorLayer_parcel]) - //console.log(true_we) + //Map Display + const map = new Map({ + target: mapDivRef.current as HTMLDivElement, + layers: [ + new TileLayer({ + source: new OSM(), + }), + ], + //End of Map Initialization - map.on('click', (e) => { - setClickedCoordinate(e.coordinate); -// Get Coordinates of click + view: new View({ + center: [-13042228, 3857849], + //center: proj_lat_long, + zoom: 12, + }), + }); + mapRef.current = map; + + + map.on('click', (e) => { + setClickedCoordinate(e.coordinate); + // Get Coordinates of click const coordinate = e.coordinate; //const hdms = toStringHDMS(toLonLat(coordinate)); + const feature = map.forEachFeatureAtPixel(e.pixel, function (feature) { + return feature; + }); -const feature = map.forEachFeatureAtPixel(e.pixel, function (feature) { - return feature; - }); - - // Show popup at clicked position - overlay.setPosition(coordinate); - if(feature) - console.log("feature " + JSON.stringify(feature,null,'\t')); - - if(feature && feature.get("subname")){ - if (popupRef.current) { - popupRef.current.innerHTML = `

Parcel

` + - `

Parcel ID ${feature.get('parcelid')}

` + - `

APN ${feature.get('apn')}

` + - `

Legldesc ${feature.get('legldesc')}

` + - `

x \& y coord${feature.get('x_coord')} / ${feature.get('y_coord')}

` + - `

${feature.get('situs_address')} ${feature.get('situs_street')} ${feature.get('situs_suffix')} ${feature.get('situs_community')} ${feature.get('situs_zip')}

` + - `

Unit Qty ${feature.get('unitqty')}

` + - `

Usable sq ft. ${feature.get('usable_sq_feet')}

` + - `

total lvg area ${feature.get('total_lvg_area')}

` + - `

bedrooms ${feature.get('bedrooms')} Baths ${feature.get('baths')}

` + - `
`; - } - } - else if(feature && feature.get('addrsfx')){ - if (popupRef.current) { - popupRef.current.innerHTML = `

You clicked here:

` + - `

Address Point

` + - - `

${feature.get('addrname')} ${feature.get('addrsfx')}

` + - `

${feature.get('addrzip')}

` + - `

${feature.get('community')}

` + - `
`; - } - } - else{ - if (popupmef.current) { - popupRef.current.innerHTML = `

You clicked here:

` + - `

Not a parcel or Address Point

` + - `
`; - } + }); - } - - overlay.setPosition(coordinate) - }); - - - // Converting lat long to mercator projection - + //Needed for Searching Location // Zoom to lat lon - if(address){ - console.log("asdf" + address[0].lat) + if (address) { + console.log("ASDF" + address[1].lat) -var ar = [address[0].lat, address[0].lon] - var proj_lat_long = fromLonLat(ar); - map.setView( + var ar = [address[1].lon, address[1].lat] + var proj_lat_long = fromLonLat(ar); + map.setView( new View({ - center: proj_lat_long, - zoom: 17 - }) - ) + center: proj_lat_long, + zoom: 15 + }) + ) } - // fit view to geometry of geojson feature with padding - //def_view.fit(geoJSONFeatures[0].getGeometry().getExtent(), { padding: [100, 100, 100, 100]}); - return () => map.setTarget(undefined); - }, [address]); + return () => map.setTarget(undefined); + }, [address]); - return ( - <> -
-
-
- setTerm(e.target.value)} - type="text" - placeholder="Enter a term" - className="input" - /> - -
-
-
-
- {clickedCoordinate && ( - - You clicked at: {clickedCoordinate[0]} /{' '} {clickedCoordinate[1]} - - )} - - ); + + +//Parcel Layer +useEffect(() => { + if (!mapRef.current) return; + +const url = 'https://geo.sandag.org/server/rest/services/Hosted/Parcels/FeatureServer/0/query' + + '?where=1=1&outFields=*&returnGeometry=true&f=geojson&resultRecordCount=5000'; + + fetch(url) + .then((res) => res.json()) + .then((geojsonData) => { + const vectorSource = new VectorSource({ + features: new GeoJSON().readFeatures(geojsonData, { + featureProjection: 'EPSG:3857', // Make sure features match OSM projection + }), + }); + + const vectorLayer = new VectorLayer({ + source: vectorSource, + style: new Style({ + stroke: new Stroke({ + color: 'green', + width: 1.5, + }), + fill: new Fill({ + color: 'rgba(0, 255, 0, 0.1)', + }), + }), + }); + + mapRef.current?.addLayer(vectorLayer); + + // ✅ Fit map to features + mapRef.current?.getView().fit(vectorSource.getExtent(), { + padding: [20, 20, 20, 20], + maxZoom: 17, + }); + }) + .catch((err) => { + console.error('Error loading GeoJSON:', err); + }); +}, []); + + + + + + + + + + + + //UI Downstairs + return ( + <> +
+
+
+ setTerm(e.target.value)} + type="text" + placeholder="Enter a term" + className="input" + /> + +
+
+
+ {clickedCoordinate && ( + + You clicked at: {clickedCoordinate[0]} /{' '} {clickedCoordinate[1]} + + )} + + ); }; export default OpenLayersMap; + + + diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 36a88f2..942dd39 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -14,7 +14,7 @@ const SearchBar = () => { console.log("print"); fetch(`https://nominatim.openstreetmap.org/search?q=${term}&format=json&polygon=1&addressdetails=1`) .then(response => response.json()) - .then(respone => console.log("printed address: " + response)) + .then(response => console.log("printed address: " + response)) } useEffect(() => { @@ -34,7 +34,7 @@ const SearchBar = () => { placeholder="Enter a term" className="input" /> -
);