diff --git a/package-lock.json b/package-lock.json index 6dfd0fd89691449cc123b3ed5a1c8d33957f2561..5226c2f4253dc71a1351e5b2b7a8d6735914dd5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@next/font": "^13.5.2", "@reduxjs/toolkit": "^1.9.6", "@types/node": "20.6.2", + "@types/openlayers": "^4.6.20", "@types/react": "18.2.21", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.15", @@ -19,6 +20,7 @@ "axios-hooks": "^5.0.0", "eslint-config-next": "13.4.19", "next": "13.4.19", + "ol": "^8.1.0", "postcss": "8.4.29", "react": "18.2.0", "react-accessible-accordion": "^5.0.0", @@ -60,7 +62,7 @@ "lint-staged": "^14.0.1", "prettier": "^3.0.3", "prettier-2": "npm:prettier@^2", - "prettier-plugin-tailwindcss": "^0.5.4", + "prettier-plugin-tailwindcss": "^0.5.6", "typescript": "^5.2.2", "zod-fixture": "^2.5.0" } @@ -1876,6 +1878,11 @@ "node": ">= 8" } }, + "node_modules/@petamoriken/float16": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/@petamoriken/float16/-/float16-3.8.4.tgz", + "integrity": "sha512-kB+NJ5Br56ZhElKsf0pM7/PQfrDdDVMRz8f0JM6eVOGE+L89z9hwcst9QvWBBnazzuqGTGtPsJNZoQ1JdNiGSQ==" + }, "node_modules/@reduxjs/toolkit": { "version": "1.9.6", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.6.tgz", @@ -2239,6 +2246,11 @@ "integrity": "sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==", "dev": true }, + "node_modules/@types/openlayers": { + "version": "4.6.20", + "resolved": "https://registry.npmjs.org/@types/openlayers/-/openlayers-4.6.20.tgz", + "integrity": "sha512-TYgdyK1WyLFcwlFkz2A7lK0stj3MFdVZeUc6+AWCmqsAHk+0o9V8TOgMqdPpb0cVIxknHwxty/g8VawWTt/FRw==" + }, "node_modules/@types/prop-types": { "version": "15.7.8", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.8.tgz", @@ -4530,6 +4542,11 @@ "node": ">=4" } }, + "node_modules/earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -6007,6 +6024,34 @@ "node": ">=6.9.0" } }, + "node_modules/geotiff": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/geotiff/-/geotiff-2.0.7.tgz", + "integrity": "sha512-FKvFTNowMU5K6lHYY2f83d4lS2rsCNdpUC28AX61x9ZzzqPNaWFElWv93xj0eJFaNyOYA63ic5OzJ88dHpoA5Q==", + "dependencies": { + "@petamoriken/float16": "^3.4.7", + "lerc": "^3.0.0", + "pako": "^2.0.4", + "parse-headers": "^2.0.2", + "quick-lru": "^6.1.1", + "web-worker": "^1.2.0", + "xml-utils": "^1.0.2" + }, + "engines": { + "node": ">=10.19" + } + }, + "node_modules/geotiff/node_modules/quick-lru": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-6.1.2.tgz", + "integrity": "sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -6509,7 +6554,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -8508,6 +8552,11 @@ "node": "> 0.8" } }, + "node_modules/lerc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lerc/-/lerc-3.0.0.tgz", + "integrity": "sha512-Rm4J/WaHhRa93nCN2mwWDZFoRVF18G1f47C+kvQWyHGEZxFpTUi73p7lMVSAndyxGt6lJ2/CFbOcf9ra5p8aww==" + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -9669,6 +9718,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ol": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/ol/-/ol-8.1.0.tgz", + "integrity": "sha512-cx3SH2plpFS9fM8pp1nCypgQXGJD7Mcb1E3mEySmy5XEw1DUEo+kkNzgtAZz5qupekqi7aU9iBJEjCoMfqvO2Q==", + "dependencies": { + "earcut": "^2.2.3", + "geotiff": "^2.0.7", + "pbf": "3.2.1", + "rbush": "^3.0.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/openlayers" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -9798,6 +9862,11 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9809,6 +9878,11 @@ "node": ">=6" } }, + "node_modules/parse-headers": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.5.tgz", + "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -9885,6 +9959,18 @@ "node": ">=8" } }, + "node_modules/pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "dependencies": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -10169,9 +10255,9 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.5.tgz", - "integrity": "sha512-voy0CjWv/CM8yeaduv5ZwovovpTGMR5LbzlhGF+LtEvMJt9wBeVTVnW781hL38R/RcDXCJwN2rolsgr94B/n0Q==", + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.6.tgz", + "integrity": "sha512-2Xgb+GQlkPAUCFi3sV+NOYcSI5XgduvDBL2Zt/hwJudeKXkyvRS65c38SB0yb9UB40+1rL83I6m0RtlOQ8eHdg==", "dev": true, "engines": { "node": ">=14.21.3" @@ -10316,6 +10402,11 @@ "react-is": "^16.13.1" } }, + "node_modules/protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -10410,6 +10501,11 @@ "node": ">=8" } }, + "node_modules/quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "node_modules/randexp": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", @@ -10423,6 +10519,14 @@ "node": ">=4" } }, + "node_modules/rbush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rbush/-/rbush-3.0.1.tgz", + "integrity": "sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==", + "dependencies": { + "quickselect": "^2.0.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -10860,6 +10964,14 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "dependencies": { + "protocol-buffers-schema": "^3.3.1" + } + }, "node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", @@ -12181,6 +12293,11 @@ "defaults": "^1.0.3" } }, + "node_modules/web-worker": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/web-worker/-/web-worker-1.2.0.tgz", + "integrity": "sha512-PgF341avzqyx60neE9DD+XS26MMNMoUQRz9NOZwW32nPQrF6p77f1htcnjBSEV8BGMKZ16choqUG4hyI0Hx7mA==" + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -12402,6 +12519,11 @@ "node": ">=12" } }, + "node_modules/xml-utils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/xml-utils/-/xml-utils-1.7.0.tgz", + "integrity": "sha512-bWB489+RQQclC7A9OW8e5BzbT8Tu//jtAOvkYwewFr+Q9T9KDGvfzC1lp0pYPEQPEoPQLDkmxkepSC/2gIAZGw==" + }, "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", diff --git a/package.json b/package.json index a7feaab56721eecfb48ad854e60c0ac989dd447d..2192fbdfc7e619cd1bd53c4d249a720130bcc24a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@next/font": "^13.5.2", "@reduxjs/toolkit": "^1.9.6", "@types/node": "20.6.2", + "@types/openlayers": "^4.6.20", "@types/react": "18.2.21", "@types/react-dom": "18.2.7", "autoprefixer": "10.4.15", @@ -33,6 +34,7 @@ "axios-hooks": "^5.0.0", "eslint-config-next": "13.4.19", "next": "13.4.19", + "ol": "^8.1.0", "postcss": "8.4.29", "react": "18.2.0", "react-accessible-accordion": "^5.0.0", @@ -74,7 +76,7 @@ "lint-staged": "^14.0.1", "prettier": "^3.0.3", "prettier-2": "npm:prettier@^2", - "prettier-plugin-tailwindcss": "^0.5.4", + "prettier-plugin-tailwindcss": "^0.5.6", "typescript": "^5.2.2", "zod-fixture": "^2.5.0" }, diff --git a/prettier.config.js b/prettier.config.js index 7946df2239da49eb1b21142cd0f61e667ceedefe..fd41c7ea28115342ff628216fd2269ac7fb37e53 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -3,7 +3,7 @@ const config = { trailingComma: 'all', printWidth: 100, arrowParens: 'avoid', - plugins: ['prettier-plugin-tailwindcss'], + plugins: [import('prettier-plugin-tailwindcss')], tailwindConfig: './tailwind.config.ts', tailwindFunctions: ['twMerge'], }; diff --git a/src/constants/map.ts b/src/constants/map.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8bde744a06a6337ec5a2dcc83aa720c364c3fd1 --- /dev/null +++ b/src/constants/map.ts @@ -0,0 +1,19 @@ +import { Point } from '@/types/map'; + +export const DEFAULT_TILE_SIZE = 256; +export const DEFAULT_MIN_ZOOM = 2; +export const DEFAULT_MAX_ZOOM = 9; +export const DEFAULT_ZOOM = 5; +export const DEFAULT_CENTER_X = 0; +export const DEFAULT_CENTER_Y = 0; + +export const DEFAULT_CENTER_POINT: Point = { + x: DEFAULT_CENTER_X, + y: DEFAULT_CENTER_Y, + z: DEFAULT_ZOOM, +}; + +export const OPTIONS = { + showFullExtent: false, + wrapXInTileLayer: false, +}; diff --git a/src/redux/map/map.constants.ts b/src/redux/map/map.constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..19bf5fb562d94a93ce85bdfac206caafe498cb96 --- /dev/null +++ b/src/redux/map/map.constants.ts @@ -0,0 +1,27 @@ +import { PROJECT_ID } from '@/constants'; +import { + DEFAULT_CENTER_POINT, + DEFAULT_MAX_ZOOM, + DEFAULT_MIN_ZOOM, + DEFAULT_TILE_SIZE, +} from '@/constants/map'; +import { MapData } from './map.types'; + +export const MAP_DATA_INITIAL_STATE: MapData = { + projectId: PROJECT_ID, + meshId: '', + modelId: 0, + overlaysIds: [], + position: DEFAULT_CENTER_POINT, + show: { + legend: false, + comments: false, + }, + size: { + width: 0, + height: 0, + tileSize: DEFAULT_TILE_SIZE, + minZoom: DEFAULT_MIN_ZOOM, + maxZoom: DEFAULT_MAX_ZOOM, + }, +}; diff --git a/src/redux/map/map.reducers.ts b/src/redux/map/map.reducers.ts new file mode 100644 index 0000000000000000000000000000000000000000..95d763254f76bcea970ec8f428e2082e80fba5d2 --- /dev/null +++ b/src/redux/map/map.reducers.ts @@ -0,0 +1,9 @@ +import { PayloadAction } from '@reduxjs/toolkit'; +import { MapData, MapState } from './map.types'; + +export const setMapDataReducer = ( + state: MapState, + action: PayloadAction<Partial<MapData> | undefined>, +): void => { + state.data = { ...state.data, ...action.payload }; +}; diff --git a/src/redux/map/map.selectors.ts b/src/redux/map/map.selectors.ts new file mode 100644 index 0000000000000000000000000000000000000000..9e559fd44e250809c36021ac18690da90cf1d92e --- /dev/null +++ b/src/redux/map/map.selectors.ts @@ -0,0 +1,4 @@ +import { createSelector } from '@reduxjs/toolkit'; +import { rootSelector } from '@/redux/root/root.selectors'; + +export const mapDataSelector = createSelector(rootSelector, state => state.map); diff --git a/src/redux/map/map.slice.ts b/src/redux/map/map.slice.ts new file mode 100644 index 0000000000000000000000000000000000000000..62b95df2be671ec12d4dec2341978c586ef235c2 --- /dev/null +++ b/src/redux/map/map.slice.ts @@ -0,0 +1,22 @@ +import { createSlice } from '@reduxjs/toolkit'; +import { MAP_DATA_INITIAL_STATE } from './map.constants'; +import { setMapDataReducer } from './map.reducers'; +import { MapState } from './map.types'; + +const initialState: MapState = { + data: MAP_DATA_INITIAL_STATE, + loading: 'idle', + error: { name: '', message: '' }, +}; + +const mapSlice = createSlice({ + name: 'map', + initialState, + reducers: { + setMapData: setMapDataReducer, + }, +}); + +export const { setMapData } = mapSlice.actions; + +export default mapSlice.reducer; diff --git a/src/redux/map/map.types.ts b/src/redux/map/map.types.ts new file mode 100644 index 0000000000000000000000000000000000000000..245bd58a7867dc4b6e3602a0079c3cbc0e63b646 --- /dev/null +++ b/src/redux/map/map.types.ts @@ -0,0 +1,29 @@ +import { Loading } from '@/types/loadingState'; +import { Point } from '@/types/map'; + +export interface MapSize { + width: number; + height: number; + tileSize: number; + minZoom: number; + maxZoom: number; +} + +export type MapData = { + projectId: string; + meshId: string; + modelId: number; + overlaysIds: number[]; + size: MapSize; + position: Point; + show: { + legend: boolean; + comments: boolean; + }; +}; + +export type MapState = { + data: MapData; + loading: Loading; + error: Error; +}; diff --git a/src/redux/store.ts b/src/redux/store.ts index 081acb384c7b0247810a8e614de74d979f8bdbc8..0136870f551d8ceb8695c6e54e45087e360f3ca6 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -1,8 +1,9 @@ -import { configureStore } from '@reduxjs/toolkit'; -import searchReducer from '@/redux/search/search.slice'; -import projectSlice from '@/redux/project/project.slice'; -import drugsReducer from '@/redux/drugs/drugs.slice'; import drawerReducer from '@/redux/drawer/drawer.slice'; +import drugsReducer from '@/redux/drugs/drugs.slice'; +import mapReducer from '@/redux/map/map.slice'; +import projectSlice from '@/redux/project/project.slice'; +import searchReducer from '@/redux/search/search.slice'; +import { configureStore } from '@reduxjs/toolkit'; export const store = configureStore({ reducer: { @@ -10,6 +11,7 @@ export const store = configureStore({ project: projectSlice, drugs: drugsReducer, drawer: drawerReducer, + map: mapReducer, }, devTools: true, }); diff --git a/src/types/map.ts b/src/types/map.ts new file mode 100644 index 0000000000000000000000000000000000000000..8dedc23f1526474dcc10d639b9e3e76312a86996 --- /dev/null +++ b/src/types/map.ts @@ -0,0 +1,7 @@ +export interface Point { + x: number; + y: number; + z?: number; +} + +export type LatLng = [number, number];