Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for local pmtiles tile package (via drag and drop) #1025

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
43 changes: 39 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"react-collapse": "^5.1.1",
"react-color": "^2.19.3",
"react-dom": "^18.2.0",
"react-dropzone": "^14.3.5",
"react-file-reader-input": "^2.0.0",
"react-i18next": "^15.4.0",
"react-icon-base": "^2.1.2",
Expand Down
12 changes: 11 additions & 1 deletion src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +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 { FileSource, PMTiles } from 'pmtiles';
import {Map, LayerSpecification, StyleSpecification, ValidationError, SourceSpecification} from 'maplibre-gl'
import {latest, validateStyleMin} from '@maplibre/maplibre-gl-style-spec'

Expand Down Expand Up @@ -131,6 +131,7 @@ type AppState = {
debug: boolean
}
fileHandle: FileSystemFileHandle | null
localPMTiles: PMTiles | null
}

export default class App extends React.Component<any, AppState> {
Expand Down Expand Up @@ -287,6 +288,7 @@ export default class App extends React.Component<any, AppState> {
debugToolbox: false,
},
fileHandle: null,
localPMTiles: null
}

this.layerWatcher = new LayerWatcher({
Expand Down Expand Up @@ -741,6 +743,7 @@ export default class App extends React.Component<any, AppState> {
onChange={this.onMapChange}
options={this.state.maplibreGlDebugOptions}
inspectModeEnabled={this.state.mapState === "inspect"}
localPMTiles={this.state.localPMTiles}
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
onLayerSelect={this.onLayerSelect} />
}
Expand Down Expand Up @@ -881,6 +884,12 @@ export default class App extends React.Component<any, AppState> {
});
}

onLocalPMTilesSelected = (file: File) => {
this.setState({
localPMTiles: new PMTiles(new FileSource(file))
})
}

render() {
const layers = this.state.mapStyle.layers || []
const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : undefined
Expand All @@ -895,6 +904,7 @@ export default class App extends React.Component<any, AppState> {
onStyleOpen={this.onStyleChanged}
onSetMapState={this.setMapState}
onToggleModal={this.toggleModal.bind(this)}
onLocalPMTilesSelected={this.onLocalPMTilesSelected}
/>

const layerList = <LayerList
Expand Down
17 changes: 17 additions & 0 deletions src/components/AppToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import maputnikLogo from 'maputnik-design/logos/logo-color.svg?inline'
import { withTranslation, WithTranslation } from 'react-i18next';
import { supportedLanguages } from '../i18n';

import Dropzone from 'react-dropzone';

// This is required because of <https://stackoverflow.com/a/49846426>, there isn't another way to detect support that I'm aware of.
const browser = detect();
const colorAccessibilityFiltersEnabled = ['chrome', 'firefox'].indexOf(browser!.name) > -1;
Expand Down Expand Up @@ -103,6 +105,7 @@ type AppToolbarInternalProps = {
onSetMapState(mapState: MapState): unknown
mapState?: MapState
renderer?: string
onLocalPMTilesSelected(file: File): unknown
} & WithTranslation;

class AppToolbarInternal extends React.Component<AppToolbarInternalProps> {
Expand Down Expand Up @@ -134,6 +137,11 @@ class AppToolbarInternal extends React.Component<AppToolbarInternalProps> {
}
}

onFileSelected = (e: File[]) => {
const file = e[0];
this.props.onLocalPMTilesSelected(file);
}

render() {
const t = this.props.t;
const views = [
Expand Down Expand Up @@ -289,6 +297,15 @@ class AppToolbarInternal extends React.Component<AppToolbarInternalProps> {
<MdHelpOutline />
<IconText>{t("Help")}</IconText>
</ToolbarLink>

<Dropzone onDrop={this.onFileSelected}>
{({getRootProps, getInputProps}) => (
<div {...getRootProps({className: 'dropzone maputnik-toolbar-link'})}>
<input {...getInputProps()} />
{t("Drop PMTiles file here")}
</div>
)}
</Dropzone>
</div>
</div>
</nav>
Expand Down
25 changes: 21 additions & 4 deletions src/components/MapMaplibreGl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +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";
import { PMTiles, Protocol } from "pmtiles";

function renderPopup(popup: JSX.Element, mountNode: ReactDOM.Container): HTMLElement {
ReactDOM.render(popup, mountNode);
Expand Down Expand Up @@ -66,6 +66,7 @@ type MapMaplibreGlInternalProps = {
}
replaceAccessTokens(mapStyle: StyleSpecification): StyleSpecification
onChange(value: {center: LngLat, zoom: number}): unknown
localPMTiles: PMTiles | null;
} & WithTranslation;

type MapMaplibreGlState = {
Expand All @@ -74,6 +75,7 @@ type MapMaplibreGlState = {
geocoder: MaplibreGeocoder | null;
zoomControl: ZoomControl | null;
zoom?: number;
pmtilesProtocol: Protocol | null;
};

class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps, MapMaplibreGlState> {
Expand All @@ -93,6 +95,7 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
inspect: null,
geocoder: null,
zoomControl: null,
pmtilesProtocol: new Protocol({metadata: true})
}
i18next.on('languageChanged', () => {
this.forceUpdate();
Expand Down Expand Up @@ -134,7 +137,21 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
this.state.inspect!.render();
}, 500);
}


if (this.props.localPMTiles) {
const file = this.props.localPMTiles;
this.state.pmtilesProtocol!.add(file); // this is necessary for non-HTTP sources

if (map) {
file.getMetadata().then((metadata: any) => {
const layerNames = metadata.vector_layers.map((e: LayerSpecification) => e.id);

// used by maplibre-gl-inspect to pick up inspectable layers
map.style.sourceCaches["source"]._source.vectorLayerIds = layerNames;
});
}
}

}

componentDidMount() {
Expand All @@ -149,8 +166,8 @@ class MapMaplibreGlInternal extends React.Component<MapMaplibreGlInternalProps,
localIdeographFontFamily: false
} satisfies MapOptions;

const protocol = new Protocol({metadata: true});
MapLibreGl.addProtocol("pmtiles",protocol.tile);
MapLibreGl.addProtocol("pmtiles", this.state.pmtilesProtocol!.tile);

const map = new MapLibreGl.Map(mapOpts);

const mapViewChange = () => {
Expand Down
2 changes: 2 additions & 0 deletions src/locales/de/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"View": "Ansicht",
"Color accessibility": "Farbzugänglichkeit",
"Help": "Hilfe",
"Drop PMTiles file here": "__STRING_NOT_TRANSLATED__",
"Comments for the current layer. This is non-standard and not in the spec.": "Kommentare zur aktuellen Ebene. Das ist nicht standardmäßig und nicht in der Spezifikation.",
"Comments": "Kommentare",
"Comment...": "Dein Kommentar...",
Expand Down Expand Up @@ -81,6 +82,7 @@
"Close modal": "Modale Fenster schließen",
"Debug": "Debug",
"Options": "Optionen",
"<0>Open in OSM</0> &mdash; Opens the current view on openstreetmap.org": "__STRING_NOT_TRANSLATED__",
"Save Style": "Stil Speichern",
"Save the JSON style to your computer.": "Speichere den JSON Stil auf deinem Computer.",
"Save as": "Speichern unter",
Expand Down
2 changes: 2 additions & 0 deletions src/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"View": "Vue",
"Color accessibility": "Accessibilité des couleurs",
"Help": "Aide",
"Drop PMTiles file here": "__STRING_NOT_TRANSLATED__",
"Comments for the current layer. This is non-standard and not in the spec.": "Commentaires pour le calque actuel. Ceci n'est pas standard et n'est pas dans la spécification.",
"Comments": "Commentaires",
"Comment...": "Votre commentaire...",
Expand Down Expand Up @@ -81,6 +82,7 @@
"Close modal": "Fermer la fenêtre modale",
"Debug": "Déboguer",
"Options": "Options",
"<0>Open in OSM</0> &mdash; Opens the current view on openstreetmap.org": "__STRING_NOT_TRANSLATED__",
"Save Style": "Enregistrer le style",
"Save the JSON style to your computer.": "Enregistrer le style JSON sur votre ordinateur.",
"Save as": "Enregistrer sous",
Expand Down
2 changes: 2 additions & 0 deletions src/locales/he/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"View": "תצוגה",
"Color accessibility": "נגישות צבעים",
"Help": "עזרה",
"Drop PMTiles file here": "__STRING_NOT_TRANSLATED__",
"Comments for the current layer. This is non-standard and not in the spec.": "הערות על השכבה הנוכחית. זה לא חלק מהספסיפיקציות",
"Comments": "הערות",
"Comment...": "הערה...",
Expand Down Expand Up @@ -81,6 +82,7 @@
"Close modal": "סגירת חלונית",
"Debug": "דיבאג",
"Options": "אפשרויות",
"<0>Open in OSM</0> &mdash; Opens the current view on openstreetmap.org": "__STRING_NOT_TRANSLATED__",
"Save Style": "שמירת הסטייל",
"Save the JSON style to your computer.": "שמירת הסטייל JSON במחשב שלך.",
"Save as": "שמירה בשם",
Expand Down
2 changes: 2 additions & 0 deletions src/locales/ja/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"View": "表示",
"Color accessibility": "色のアクセシビリティ",
"Help": "ヘルプ",
"Drop PMTiles file here": "__STRING_NOT_TRANSLATED__",
"Comments for the current layer. This is non-standard and not in the spec.": "現在のレイヤーのコメント。注意:この機能は標準ではないため、他のライブラリとの互換性状況はわかりません。",
"Comments": "コメント",
"Comment...": "コメントを書く",
Expand Down Expand Up @@ -81,6 +82,7 @@
"Close modal": "モーダルを閉じる",
"Debug": "デバッグ",
"Options": "設定",
"<0>Open in OSM</0> &mdash; Opens the current view on openstreetmap.org": "__STRING_NOT_TRANSLATED__",
"Save Style": "スタイルを保存",
"Save the JSON style to your computer.": "JSONスタイルをコンピュータに保存します。",
"Save as": "名前を付けて保存",
Expand Down
2 changes: 2 additions & 0 deletions src/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"View": "视图",
"Color accessibility": "颜色可访问性",
"Help": "帮助",
"Drop PMTiles file here": "__STRING_NOT_TRANSLATED__",
"Comments for the current layer. This is non-standard and not in the spec.": "当前图层的注释。注意:这不是标准功能,可能与其他库不兼容。",
"Comments": "注释",
"Comment...": "写注释...",
Expand Down Expand Up @@ -81,6 +82,7 @@
"Close modal": "关闭模态框",
"Debug": "调试",
"Options": "选项",
"<0>Open in OSM</0> &mdash; Opens the current view on openstreetmap.org": "__STRING_NOT_TRANSLATED__",
"Save Style": "保存样式",
"Save the JSON style to your computer.": "将JSON样式保存到您的计算机。",
"Save as": "另存为",
Expand Down
Loading