mirror of
https://github.com/maputnik/editor.git
synced 2026-08-28 16:07:28 +00:00
Compare commits
17 Commits
main
...
60dea0309c
| Author | SHA1 | Date | |
|---|---|---|---|
| 60dea0309c | |||
| 65cf85ccc7 | |||
| 6fbc79c5f5 | |||
| 51c5bb7f7f | |||
| cf423c39da | |||
| df4c417b3b | |||
| 0dba0721ed | |||
| 6199b808d0 | |||
| 616c9aa24a | |||
| 292666a294 | |||
| 189c34e0a2 | |||
| 4087353219 | |||
| 6e8488f360 | |||
| de670771d8 | |||
| 5f28b250ec | |||
| 498b7d76de | |||
| f9db0d9b5c |
@@ -83,6 +83,24 @@ describe("modals", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("add new pmtiles source", () => {
|
||||
let sourceId = "pmtilestest";
|
||||
when.setValue("modal:sources.add.source_id", sourceId);
|
||||
when.select("modal:sources.add.source_type", "pmtiles_vector");
|
||||
when.setValue("modal:sources.add.source_url", "https://data.source.coop/protomaps/openstreetmap/v4.pmtiles");
|
||||
when.click("modal:sources.add.add_source");
|
||||
when.click("modal:sources.add.add_source");
|
||||
when.wait(200);
|
||||
then(get.styleFromLocalStorage()).shouldDeepNestedInclude({
|
||||
sources: {
|
||||
pmtilestest: {
|
||||
type: "vector",
|
||||
url: "pmtiles://https://data.source.coop/protomaps/openstreetmap/v4.pmtiles",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("add new raster source", () => {
|
||||
let sourceId = "rastertest";
|
||||
when.setValue("modal:sources.add.source_id", sourceId);
|
||||
|
||||
Generated
+3825
-2703
File diff suppressed because it is too large
Load Diff
+40
-41
@@ -9,7 +9,7 @@
|
||||
"build": "tsc && vite build --base=/maputnik/",
|
||||
"build-desktop": "tsc && vite build --base=/ && cd desktop && make",
|
||||
"i18n:refresh": "i18next 'src/**/*.{ts,tsx,js,jsx}'",
|
||||
"lint": "eslint ./src ./cypress --ext ts,tsx,js,jsx --report-unused-disable-directives --max-warnings 0",
|
||||
"lint": "eslint",
|
||||
"test": "cypress run",
|
||||
"cy:open": "cypress open",
|
||||
"lint-css": "stylelint \"src/styles/*.scss\"",
|
||||
@@ -23,25 +23,22 @@
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/maplibre/maputnik#readme",
|
||||
"dependencies": {
|
||||
"@mapbox/mapbox-gl-rtl-text": "^0.2.3",
|
||||
"@maplibre/maplibre-gl-geocoder": "^1.6.0",
|
||||
"@maplibre/maplibre-gl-inspect": "^1.6.3",
|
||||
"@maplibre/maplibre-gl-style-spec": "^20.1.1",
|
||||
"@mapbox/mapbox-gl-rtl-text": "^0.3.0",
|
||||
"@maplibre/maplibre-gl-geocoder": "^1.7.1",
|
||||
"@maplibre/maplibre-gl-inspect": "^1.7.1",
|
||||
"@maplibre/maplibre-gl-style-spec": "^23.1.0",
|
||||
"@mdi/js": "^7.4.47",
|
||||
"@mdi/react": "^1.6.1",
|
||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
||||
"@typescript-eslint/parser": "^7.3.1",
|
||||
"array-move": "^4.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"classnames": "^2.5.1",
|
||||
"codemirror": "^5.65.2",
|
||||
"color": "^4.2.3",
|
||||
"cypress-plugin-tab": "^1.0.5",
|
||||
"detect-browser": "^5.3.0",
|
||||
"events": "^3.3.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"i18next": "^23.12.2",
|
||||
"i18next-browser-languagedetector": "^8.0.0",
|
||||
"i18next": "^24.2.1",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"i18next-resources-to-backend": "^1.2.1",
|
||||
"json-stringify-pretty-compact": "^4.0.0",
|
||||
"json-to-ast": "^2.1.0",
|
||||
@@ -53,10 +50,11 @@
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"maplibre-gl": "^4.1.2",
|
||||
"maplibre-gl": "^5.0.1",
|
||||
"maputnik-design": "github:maputnik/design#172b06c",
|
||||
"ol": "^6.14.1",
|
||||
"ol-mapbox-style": "^7.1.1",
|
||||
"ol": "^10.3.1",
|
||||
"ol-mapbox-style": "^12.4.0",
|
||||
"pmtiles": "^4.1.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-accessible-accordion": "^5.0.0",
|
||||
@@ -68,15 +66,14 @@
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-file-reader-input": "^2.0.0",
|
||||
"react-i18next": "^15.0.1",
|
||||
"react-i18next": "^15.4.0",
|
||||
"react-icon-base": "^2.1.2",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-icons": "^5.4.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"sass": "^1.72.0",
|
||||
"slugify": "^1.6.6",
|
||||
"string-hash": "^1.1.3",
|
||||
"url": "^0.11.3"
|
||||
"url": "^0.11.4"
|
||||
},
|
||||
"jshintConfig": {
|
||||
"esversion": 6
|
||||
@@ -96,12 +93,13 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/code-coverage": "^3.12.30",
|
||||
"@cypress/code-coverage": "^3.13.10",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||
"@rollup/plugin-replace": "^5.0.5",
|
||||
"@shellygo/cypress-test-utils": "^2.1.9",
|
||||
"@rollup/plugin-replace": "^6.0.2",
|
||||
"@shellygo/cypress-test-utils": "^4.1.11",
|
||||
"@types/codemirror": "^5.60.15",
|
||||
"@types/color": "^3.0.6",
|
||||
"@types/color": "^4.2.0",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/geojson": "^7946.0.14",
|
||||
@@ -112,39 +110,40 @@
|
||||
"@types/lodash.get": "^4.4.9",
|
||||
"@types/lodash.isequal": "^4.5.8",
|
||||
"@types/lodash.throttle": "^4.1.9",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/randomcolor": "^0.5.9",
|
||||
"@types/react": "^18.2.67",
|
||||
"@types/react-aria-menubutton": "^6.2.14",
|
||||
"@types/react-aria-modal": "^4.0.10",
|
||||
"@types/react-autocomplete": "^1.8.10",
|
||||
"@types/react-aria-modal": "^5.0.0",
|
||||
"@types/react-autocomplete": "^1.8.11",
|
||||
"@types/react-collapse": "^5.0.4",
|
||||
"@types/react-color": "^3.0.12",
|
||||
"@types/react-color": "^3.0.13",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@types/react-file-reader-input": "^2.0.4",
|
||||
"@types/react-icon-base": "^2.1.6",
|
||||
"@types/string-hash": "^1.1.3",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@types/wicg-file-system-access": "^2023.10.5",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"cors": "^2.8.5",
|
||||
"cypress": "^13.13.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"i18next-parser": "^9.0.1",
|
||||
"cypress": "^14.0.0",
|
||||
"cypress-plugin-tab": "^1.0.5",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.18",
|
||||
"i18next-parser": "^9.1.0",
|
||||
"istanbul": "^0.4.5",
|
||||
"istanbul-lib-coverage": "^3.2.2",
|
||||
"mocha": "^10.3.0",
|
||||
"postcss": "^8.4.38",
|
||||
"react-hot-loader": "^4.13.1",
|
||||
"stylelint": "^16.2.1",
|
||||
"stylelint-config-recommended-scss": "^14.0.0",
|
||||
"stylelint-scss": "^6.2.1",
|
||||
"typescript": "^5.4.3",
|
||||
"uuid": "^9.0.1",
|
||||
"vite": "^5.4.6",
|
||||
"vite-plugin-istanbul": "^6.0.0"
|
||||
"sass": "^1.83.4",
|
||||
"stylelint": "^16.13.2",
|
||||
"stylelint-config-recommended-scss": "^14.1.0",
|
||||
"stylelint-scss": "^6.10.1",
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.21.0",
|
||||
"uuid": "^11.0.5",
|
||||
"vite": "^6.0.11",
|
||||
"vite-plugin-istanbul": "^6.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
+34
-25
@@ -8,6 +8,7 @@ import get from 'lodash.get'
|
||||
import {unset} from 'lodash'
|
||||
import {arrayMoveMutable} from 'array-move'
|
||||
import hash from "string-hash";
|
||||
import { PMTiles } from "pmtiles";
|
||||
import {Map, LayerSpecification, StyleSpecification, ValidationError, SourceSpecification} from 'maplibre-gl'
|
||||
import {latest, validateStyleMin} from '@maplibre/maplibre-gl-style-spec'
|
||||
|
||||
@@ -641,33 +642,41 @@ export default class App extends React.Component<any, AppState> {
|
||||
console.warn("Failed to setFetchAccessToken: ", err);
|
||||
}
|
||||
|
||||
fetch(url!, {
|
||||
mode: 'cors',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
const setVectorLayers = (json:any) => {
|
||||
if(!Object.prototype.hasOwnProperty.call(json, "vector_layers")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!Object.prototype.hasOwnProperty.call(json, "vector_layers")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new objects before setState
|
||||
const sources = Object.assign({}, {
|
||||
[key]: this.state.sources[key],
|
||||
});
|
||||
|
||||
for(const layer of json.vector_layers) {
|
||||
(sources[key] as any).layers.push(layer.id)
|
||||
}
|
||||
|
||||
console.debug("Updating source: "+key);
|
||||
this.setState({
|
||||
sources: sources
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error("Failed to process sources for '%s'", url, err);
|
||||
// Create new objects before setState
|
||||
const sources = Object.assign({}, {
|
||||
[key]: this.state.sources[key],
|
||||
});
|
||||
|
||||
for(const layer of json.vector_layers) {
|
||||
(sources[key] as any).layers.push(layer.id)
|
||||
}
|
||||
|
||||
this.setState({
|
||||
sources: sources
|
||||
});
|
||||
};
|
||||
|
||||
if (url!.startsWith("pmtiles://")) {
|
||||
(new PMTiles(url!.substr(10))).getTileJson("")
|
||||
.then(json => setVectorLayers(json))
|
||||
.catch(err => {
|
||||
console.error("Failed to process sources for '%s'", url, err);
|
||||
});
|
||||
} else {
|
||||
fetch(url!, {
|
||||
mode: 'cors',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => setVectorLayers(json))
|
||||
.catch(err => {
|
||||
console.error("Failed to process sources for '%s'", url, err);
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
sourceList[key] = this.state.sources[key] || this.state.mapStyle.sources[key];
|
||||
|
||||
@@ -15,6 +15,7 @@ import MaplibreGeocoder, { MaplibreGeocoderApi, MaplibreGeocoderApiConfig } from
|
||||
import '@maplibre/maplibre-gl-geocoder/dist/maplibre-gl-geocoder.css';
|
||||
import { withTranslation, WithTranslation } from 'react-i18next'
|
||||
import i18next from 'i18next'
|
||||
import { Protocol } from "pmtiles";
|
||||
|
||||
function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container): HTMLElement {
|
||||
ReactDOM.render(popup, mountNode);
|
||||
@@ -148,6 +149,8 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
|
||||
localIdeographFontFamily: false
|
||||
} satisfies MapOptions;
|
||||
|
||||
let protocol = new Protocol({metadata: true});
|
||||
MapLibreGl.addProtocol("pmtiles",protocol.tile);
|
||||
const map = new MapLibreGl.Map(mapOpts);
|
||||
|
||||
const mapViewChange = () => {
|
||||
|
||||
@@ -51,6 +51,7 @@ function editorMode(source: SourceSpecification) {
|
||||
}
|
||||
if(source.type === 'vector') {
|
||||
if(source.tiles) return 'tile_vector'
|
||||
if(source.url && source.url.startsWith("pmtiles://")) return 'pmtiles_vector'
|
||||
return 'tilejson_vector'
|
||||
}
|
||||
if(source.type === 'geojson') {
|
||||
@@ -129,6 +130,10 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
|
||||
const {protocol} = window.location;
|
||||
|
||||
switch(mode) {
|
||||
case 'pmtiles_vector': return {
|
||||
type: 'vector',
|
||||
url: `${protocol}//localhost:3000/file.pmtiles`
|
||||
}
|
||||
case 'geojson_url': return {
|
||||
type: 'geojson',
|
||||
data: `${protocol}//localhost:3000/geojson.json`
|
||||
@@ -240,6 +245,7 @@ class AddSource extends React.Component<AddSourceProps, AddSourceState> {
|
||||
['tile_raster', t('Raster (Tile URLs)')],
|
||||
['tilejson_raster-dem', t('Raster DEM (TileJSON URL)')],
|
||||
['tilexyz_raster-dem', t('Raster DEM (XYZ URLs)')],
|
||||
['pmtiles_vector', t('Vector (PMTiles)')],
|
||||
['image', t('Image')],
|
||||
['video', t('Video')],
|
||||
]}
|
||||
|
||||
@@ -11,7 +11,7 @@ import FieldCheckbox from './FieldCheckbox'
|
||||
import { WithTranslation, withTranslation } from 'react-i18next';
|
||||
import { TFunction } from 'i18next'
|
||||
|
||||
export type EditorMode = "video" | "image" | "tilejson_vector" | "tile_raster" | "tilejson_raster" | "tilexyz_raster-dem" | "tilejson_raster-dem" | "tile_vector" | "geojson_url" | "geojson_json" | null;
|
||||
export type EditorMode = "video" | "image" | "tilejson_vector" | "tile_raster" | "tilejson_raster" | "tilexyz_raster-dem" | "tilejson_raster-dem" | "pmtiles_vector" | "tile_vector" | "geojson_url" | "geojson_json" | null;
|
||||
|
||||
type TileJSONSourceEditorProps = {
|
||||
source: {
|
||||
@@ -286,6 +286,33 @@ class GeoJSONSourceFieldJsonEditor extends React.Component<GeoJSONSourceFieldJso
|
||||
}
|
||||
}
|
||||
|
||||
type PMTilesSourceEditorProps = {
|
||||
source: {
|
||||
url: string
|
||||
}
|
||||
onChange(...args: unknown[]): unknown
|
||||
children?: React.ReactNode
|
||||
} & WithTranslation;
|
||||
|
||||
class PMTilesSourceEditor extends React.Component<PMTilesSourceEditorProps> {
|
||||
render() {
|
||||
const t = this.props.t;
|
||||
return <div>
|
||||
<FieldUrl
|
||||
label={t("PMTiles URL")}
|
||||
fieldSpec={latest.source_vector.url}
|
||||
value={this.props.source.url}
|
||||
data-wd-key="modal:sources.add.source_url"
|
||||
onChange={(url: string) => this.props.onChange({
|
||||
...this.props.source,
|
||||
url: url.startsWith("pmtiles://") ? url : `pmtiles://${url}`
|
||||
})}
|
||||
/>
|
||||
{this.props.children}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
type ModalSourcesTypeEditorInternalProps = {
|
||||
mode: EditorMode
|
||||
source: any
|
||||
@@ -343,6 +370,7 @@ class ModalSourcesTypeEditorInternal extends React.Component<ModalSourcesTypeEdi
|
||||
value={this.props.source.encoding || latest.source_raster_dem.encoding.default}
|
||||
/>
|
||||
</TileURLSourceEditor>
|
||||
case 'pmtiles_vector': return <PMTilesSourceEditor {...commonProps} />
|
||||
case 'image': return <ImageSourceEditor {...commonProps} />
|
||||
case 'video': return <VideoSourceEditor {...commonProps} />
|
||||
default: return null
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"Raster (Tile URLs)": "Raster (Tile URLs)",
|
||||
"Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)",
|
||||
"Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)",
|
||||
"Vector (PMTiles)": "Vektor (PMTiles)",
|
||||
"Image": "Bild",
|
||||
"Video": "Video",
|
||||
"Add Source": "Quelle hinzufügen",
|
||||
@@ -170,6 +171,7 @@
|
||||
"GeoJSON URL": "GeoJSON URL",
|
||||
"GeoJSON": "GeoJSON",
|
||||
"Cluster": "Cluster",
|
||||
"PMTiles URL": "PMTiles URL",
|
||||
"Tile Size": "Kachelgröße",
|
||||
"Encoding": "Kodierung",
|
||||
"Error:": "Fehler:",
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"Raster (Tile URLs)": "Raster (URLs Tile)",
|
||||
"Raster DEM (TileJSON URL)": "Raster DEM (URL TileJSON)",
|
||||
"Raster DEM (XYZ URLs)": "Raster DEM (URLs XYZ)",
|
||||
"Vector (PMTiles)": "Vecteur (PMTiles)",
|
||||
"Image": "Image",
|
||||
"Video": "Vidéo",
|
||||
"Add Source": "Ajouter une source",
|
||||
@@ -170,6 +171,7 @@
|
||||
"GeoJSON URL": "URL GeoJSON",
|
||||
"GeoJSON": "GeoJSON",
|
||||
"Cluster": "Cluster",
|
||||
"PMTiles URL": "URL PMTiles",
|
||||
"Tile Size": "Dimension d'une tuile",
|
||||
"Encoding": "Encodage",
|
||||
"Error:": "Erreur :",
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"Raster (Tile URLs)": "Raster (Tile URLs)",
|
||||
"Raster DEM (TileJSON URL)": "Raster DEM (TileJSON URL)",
|
||||
"Raster DEM (XYZ URLs)": "Raster DEM (XYZ URLs)",
|
||||
"Vector (PMTiles)": "Vector (PMTiles)",
|
||||
"Image": "תמונה",
|
||||
"Video": "וידאו",
|
||||
"Add Source": "הוספת מקור",
|
||||
@@ -170,6 +171,7 @@
|
||||
"GeoJSON URL": "כתובת GeoJSON",
|
||||
"GeoJSON": "GeoJSON",
|
||||
"Cluster": "קיבוץ",
|
||||
"PMTiles URL": "כתובת PMTiles",
|
||||
"Tile Size": "גודל אריח",
|
||||
"Encoding": "קידוד",
|
||||
"Error:": "שגיאה",
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"Raster (Tile URLs)": "ラスタ (Tile URLs)",
|
||||
"Raster DEM (TileJSON URL)": "ラスタ DEM (TileJSON URL)",
|
||||
"Raster DEM (XYZ URLs)": "ラスタ DEM (XYZ URL)",
|
||||
"Vector (PMTiles)": "__STRING_NOT_TRANSLATED__",
|
||||
"Image": "画像",
|
||||
"Video": "動画",
|
||||
"Add Source": "ソースを追加",
|
||||
@@ -170,6 +171,7 @@
|
||||
"GeoJSON URL": "GeoJSON URL",
|
||||
"GeoJSON": "GeoJSON",
|
||||
"Cluster": "クラスタ",
|
||||
"PMTiles URL": "__STRING_NOT_TRANSLATED__",
|
||||
"Tile Size": "タイルサイズ",
|
||||
"Encoding": "エンコーディング",
|
||||
"Error:": "エラー:",
|
||||
|
||||
@@ -149,6 +149,7 @@
|
||||
"Raster (Tile URLs)": "栅格数据 (Tile URLs)",
|
||||
"Raster DEM (TileJSON URL)": "栅格高程数据 (TileJSON URL)",
|
||||
"Raster DEM (XYZ URLs)": "栅格高程数据 (XYZ URLs)",
|
||||
"Vector (PMTiles)": "__STRING_NOT_TRANSLATED__",
|
||||
"Image": "图像",
|
||||
"Video": "视频",
|
||||
"Add Source": "添加源",
|
||||
@@ -170,6 +171,7 @@
|
||||
"GeoJSON URL": "GeoJSON URL",
|
||||
"GeoJSON": "GeoJSON",
|
||||
"Cluster": "聚合",
|
||||
"PMTiles URL": "__STRING_NOT_TRANSLATED__",
|
||||
"Tile Size": "__STRING_NOT_TRANSLATED__",
|
||||
"Encoding": "编码",
|
||||
"Error:": "错误:",
|
||||
|
||||
Reference in New Issue
Block a user