gishub4 / solid-map-gl Goto Github PK
View Code? Open in Web Editor NEWSolid JS Components Library for Mapbox & MapLibre π
Home Page: https://gis-hub.gitbook.io/solid-map-gl/
License: MIT License
Solid JS Components Library for Mapbox & MapLibre π
Home Page: https://gis-hub.gitbook.io/solid-map-gl/
License: MIT License
Thanks for the great project.
Is it possible to add a custom protocol as shown in this example? I'm trying to load PMTiles specifically.
On the examples page, there's an example called Sync Maps. It doesn't work.
Swipe Maps looks very similar, and also doesn't work.
The maps simply don't update each other.
I have the need for imperatively switching a CustomLayer on and off, unfortunately the visible
Prop has no effect here and results in a thrown error if switched dynamically with a signal:
Error: The layer 'cl-168' does not exist in the map's style and cannot be styled.
The onLoad
event on MapGL is not getting fired. I did a quick inspection of the source code and found that the events only get attached after the "load" has fired and hence the "onLoad" never gets fired as the "load" event is already finished.
I would like to set some event listeners on a specific layer via the 3 argument option such as in onMouseEnter
i.e.
https://docs.mapbox.com/mapbox-gl-js/api/map/#map.event:mouseenter
map.on('mouseenter', 'mylayer', () => console.log('foo'))
From what i'm seeing here: https://github.com/GIShub4/solid-map-gl/blob/main/src/components/MapGL/index.tsx#L148 is doesn't seem like 3 arguments are supported by solid-map-gl, just the event and callback style. In some cases certain events allow 2 or 3 arguments e..g dblclick
. Am I missing something? Happy to contribute if this is really not currently supported and there would be interest in building in support for these event handlers.
<Marker
lngLat={[centerLng() || 0, centerLat() || 0]}
showPopup
popup={{ closeButton: false }}
>
Hi
I trued to use like above, but popup is not open by default and when clicked, popup opens with close button.
Above prop types was different in 1.9.0
Sorry to flood you with issues! None of these are critical though, thanks goodness. I noticed that my implementation is showing attributions for both MapLibre and Mapbox:
I don't actually have MapLibre populated and instead have my Mapbox key. I do have both libraries installed because it complains if they aren't both added. Is there a way to shut both of them off?
Hi, should there be a 'Viewport' component? It is used in the example code but not in the install. I'm not using Typescript at the moment so maybe that is my problem
Thank you
Hi again!
I'd like to keep track of the viewpoint in a Store, so that the user can navigate to another page (using the router) but when they return have the map in the same place as they left it.
This is pretty similar to #1. Some sample code:
const Map: Component = () => {
const [store, { setViewport }] = useStore();
const viewport = () => store.map.viewport;
return (
<MapGL
class={styles.Map}
options={{
accessToken: <my access token here>,
style: <style URL here>,
}}
viewport={viewport()}
></MapGL>
);
};
I had a look at the changes you made in 808429d, and was going to put together a pull request. But it seems a bit more complex for viewports because you have the callMove
and callEnd
functions, and I thought I would get lost.
Thanks!
I would like to use a custom Marker
. There is the option to provide a HTMLElement but this currently only works with document.createElement
... any chance to use Solid components for this? Maybe there is already a way but my soildjs knowledge seems not solid enough π
This works:
const myDiv = document.createElement("div");
const newContent = document.createTextNode("Hi there and greetings!");
myDiv.appendChild(newContent);
...
<Marker
lngLat={[0, 0]}
options={{ element: myDiv }}
>
Hi there! π
</Marker>
This doesn't:
const TestDiv = () => <div>Hi there and greetings!</div>;
...
<Marker
lngLat={[0, 0]}
options={{ element: <TestDiv /> }}
>
Hi there! π
</Marker>
The console error I get with the second example: Uncaught TypeError: Comp is not a function ...
I would like to create some concentric circles outward around a point (a selection), designating distances from the center visually.
Since I wasn't able to find anything specific for doing this, I created some Postgres PostGIS that calculates using:
ST_AsGeoJSON(ST_Buffer(ST_SetSRID(ST_MakePoint(${station.longitude}, ${station.latitude}), 4326), 0.5, 'quad_segs=16'))
However this draws an oval on the globe.
I'm wondering if there's a better way to handle this?
Rendering a
Also getting below error as well. There is no error if we do not use <Source>
.
TypeError: Cannot read properties of undefined (reading 'getLayer')
This issue is only when using Source and Layer.
I would like to to about 20 different SVGs as symbols. How can this be done?
Is there a way to get the current MapboxMap
instance (I want to call fitBounds()
on it) from an existing MapGL instance in a Component?
Hi! I was reviewing SolidHack submissions and noticed your work on this project. I attempted a very simple implementation myself a few months back for a project but your is far more advanced.
I'd love to offer you the solid-mapbox handle on GitHub and NPM if you'd take it. I think this project deserves to be in its place. Let me know and I'd be happy to transfer.
I tried the example where the buttons existed to change the map type, but the buttons never showed up.
I'm not sure this is intentional, but when the style of the map is changed in globe mode, the atmosphere and all markers from GeoJSON layers are cleared.
I have a MapGL map with a marker in it which all works fine. Now I want to allow the user to drag the marker.
I did set the marker option draggable: true
as described at https://docs.mapbox.com/mapbox-gl-js/api/markers/#marker but the marker stays put. I am using the default marker CSS. Also the onDragEnd
event of the marker does not fire.
Is that a known issue or am I doing it wrong somehow?
After installing Node.js and pnpm on Windows 10, and running these:
pnpm create solid && pnpm i
pnpm add solid-map-gl maplibre-gl mapbox-gl@npm:[email protected]
the 'pnpm dev' command gives error:
'dev' is not recognized as an internal or external command, operable program or batch file.
ERR_PNPM_RECURSIVE_EXEC_FIRST_FAILβ Command "dev" not found
Any ideas? Thanks.
How can we hook into hover and click for points that are in a GeoJSON defined by a Source/Layer combo? Would be great to be able to show a minimal popup on hover, and instruct a more formal sticky div for click.
Hi! Following up on this request @birkskyum I'd love to see MapLibre support alongside Mapbox. I'm a big fan of both products and actively use solid-map-gl in two projects now. My next one could use MapLibre because there's no budget for the service
Could this just be a drop-in replacement? Perhaps MapGL could accept an option to override the library internally?
by default the map container has a z-index
of -1
, this requires the parent element to have a higher z-index than 1.
To overcome this, I tried using the class prop (I'm using tailwind for styling). But if anything is supplied I the page spam creates new lines, overflowing the viewport and the map is never rendered.
it would be ideal id it acted like a normal div and would inherit it's parent's available space
I just stumbled on a package that can handle map library agnostic drawing that might be useful and relevant: https://github.com/JamesLMilner/terra-draw
Something to consider for a future addition :)
It would be wonderful if there was a way to specify if the pop-up should be displayed on load or not ie:
<Marker
lngLat={[location.longitude, location.latitude]}
options={{ color: "#000", focusAfterOpen: true, open: false }}
open={false}
>
{location.name}
</Marker>
The issue I'm running into is I want the popups to only display when they are clicked on, right now I can't do so without writing my own Popup component.
There's an issue with giving marker a new position. Inside MapGL component I have set marker like this
<Marker lngLat={viewport().center} options={{ color: "#F00" }}></Marker>
However if I update viewport center marker won't rerender in new position.
This configuration will produce markers that go out of map bounds, wrong places etc.
Is this a bug or am I configuring the library incorrectly? Thanks.
import MapGL, { Layer, Marker, Source, Viewport } from "solid-map-gl";
import * as maplibre from 'maplibre-gl';
export default function Home() {
const [viewport, setViewport] = createSignal({
center: [0, 50],
zoom: 15,
} as Viewport);
const style = {
"version": 8,
"sources": {
"osm": {
"type": "raster",
"tiles": ["https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"],
"tileSize": 256,
"maxzoom": 19
}
},
"layers": [
{
"id": "osm",
"type": "raster",
"source": "osm" // This must match the source key above
}
]
};
return (
<main>
<Title>My App</Title>
<h1>My App</h1>
<MapGL style={{width: "800px", height: "800px"}}
options={{style: style}}
mapLib={maplibre}
viewport={viewport()}
onViewportChange={(evt: Viewport) => setViewport(evt)}
>
<For each={data()!.startLocs}>{(loc, i) =>
<Marker lngLat={loc} options={{ color: '#F00' }}>
Hi there! π
</Marker>
}</For>
</MapGL>
</main>
);
}
Drawing GeoJSON Source works fine with the same config so I assume Marker has some bugs.
<Source
source={{
type: 'geojson',
data: toGeoJSON(data()!.locs)
}}
>
<Layer
style={{
type: 'circle',
paint: {
'color': 'red',
},
}}
/>
</Source>
Changes that do not help:
Property 'image' does not exist on type 'IntrinsicAttributes & Props & { children?: undefined; }'.ts(2322)
import { render } from "solid-js/web";
import { Component, createSignal } from "solid-js";
import MapGL, { Viewport, Source, Image, Layer } from "solid-map-gl";
import 'mapbox-gl/dist/mapbox-gl.css'
const MAPBOX_ACCESS_TOKEN = "<my key>";
const App: Component = () => {
const [viewport, setViewport] = createSignal({
center: [18.469036393097564, -33.996130386130154],
zoom: 11,
} as Viewport);
return (
<MapGL
options={{ style: "mb:light", accessToken: MAPBOX_ACCESS_TOKEN }}
viewport={viewport()}
onViewportChange={(evt: Viewport) => setViewport(evt)}
>
<Image
id="olarm"
image="/src/assets/favicon.png"></Image>
<Source
source={{
type: "geojson",
data: "https://olarm-resellers.s3.af-south-1.amazonaws.com/resellers.json",
}}
>
<Layer
style={{
type: "symbol",
layout: {'icon-image': 'olarm', 'icon-size': 0.25 }
}}
/>
</Source>
</MapGL>
);
};
export default App;
Running trying the maplibre example, both npm and yarn are unable to resolve dpendencies:
NPM:
β― npm i
npm ERR! code ERESOLVE
npm ERR! ERESOLVE unable to resolve dependency tree
npm ERR!
npm ERR! While resolving: [email protected]
npm ERR! Found: [email protected]
npm ERR! node_modules/mapbox-gl
npm ERR! mapbox-gl@"npm:[email protected]" from the root project
npm ERR!
npm ERR! Could not resolve dependency:
npm ERR! peer mapbox-gl@"^2.10.0" from [email protected]
npm ERR! node_modules/solid-map-gl
npm ERR! solid-map-gl@"^1.4.9" from the root project
npm ERR!
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
Yarn 1:
...
warning " > [email protected]" has incorrect peer dependency "mapbox-gl@^2.10.0".
...
I've used react-map-gl wrapper before which worked fine probably because it's less strict on version of mapbox.
When setting up a simple test project, I get following error:
Steps to repoduce:
npx degit solidjs/templates/ts my-app
npm install --save solid-map-gl
npm start
It looks like a bundler issue, however I'm not very familiar with that. Might be related to this:
mapbox/mapbox-gl-js#8676
I tried to get both examples on the README to work and after a yarn add mabox-gl
all module import errors were fixed.
But now I have the following issue (interestingly it happens on the Mapbox AND on the MapLibre example?):
index.jsx? [sm]:3 Uncaught (in promise)
SyntaxError: The requested module '/node_modules/mapbox-gl/dist/mapbox-gl.js?v=d097f78f'
does not provide an export named 'default' (at index.jsx? [sm]:3:8)
The dev console identifies this line
Could you point me towards a direction how to solve it?
I would like to make bounding box content loads when the viewport changes. What is the best methodology to do this?
Is there an equivalent to z-index
for layers? I noticed that when drawing some GeoJSON content with opacity, that markers from other layers are respected at outward zooms, but as the zoom comes in, the items appear more behind the lower opacity content.
I found a reference in the docs that claims the onViewportChange property accepts a function with Event instead of Viewport: https://gis-hub.gitbook.io/solid-map-gl/components/mapgl#interactive-map
My apologies for not submitting this as a PR. I'll admit I'm rushing through a project right now and it's quicker for me to report as an issue. If I have a moment I can circle back to this in a few weeks and help with a proper PR.
onViewportChange={(evt: Event) => setViewport(evt)}
Should be
onViewportChange={(viewport: Viewport) => setViewport(viewport)}
Thanks!
Looks like Layer Style minzoom
and maxzoom
are broken, same as the Layer visible
prop (see issue #94).
solid-map-gl/src/components/Layer/index.tsx
Line 150 in fc35853
We can expect featureState.id
can be 0
when generateId = true
so in that case id
will correspond to the array index of the feature.
I need to provide my map with some GeoJSON data. I am running the example from https://gis-hub.gitbook.io/solid-map-gl/components/source#geojson-source by copy & paste in an App.tsx
file and the output in my browser's console is
I am 100% sure that the format is correct since I used it also in a Mapbox React project. Is it is a bug?
Addendum: the same error happens if I turn the tsx
file into a jsx
file and remove all types.
Currently, event handlers are not part of map or layer props. When used in a strict TS project, adding event handlers to props fails type checking. I also wasn't able to augment module declarations probably due to props types not being originally declared as interfaces.
Maybe I'm missing something since this project is written in TypeScript. I also noticed that mapbox-gl is typed in Flow and you don't use @types/mapbox-gl in the project. Just wonder what's your main use case for the library and how you go about typings.
If you insert HTML content, e.g. a <p>
element within a Marker
, you will only get [object HTMLParagraphElement]
instead of the HTML content. I haven't found any workaround to this. A custom component doesn't work either inside a Marker. Only text content seems to work.
<Show when={locs()}>
<For each={locs()}>{(loc, i) =>
<Marker lngLat={loc.geometry?.coordinates} options={{ color: 'red' }}>
<p>{loc.properties.osm_tags?.name}</p>
</Marker>
}</For>
</Show>
The above example renders [object HTMLParagraphElement]
to the Marker
s.
Any ideas? Thanks.
Thank you for very nice tool. I noticed some bugs while testing. Sync Maps Demo (https://determined-mirzakhani-29bbc7.netlify.app/) doesn't seem to be working on Mac OS 11.6.4 (tested on Chrome and Safari). If I move one of the maps, other maps won't update viewport to same position.
Also I didn't manage to get ref prop working.
const [viewport, setViewport] = createSignal({
center: [138.74, 35.3],
zoom: 11,
pitch: 70,
} as Viewport);
let mapRef;
createEffect(() => console.log(mapRef))
return (
<MapGL
options={{
accessToken: MAPBOX_ACCESS_TOKEN,
style: "mapbox://styles/mapbox/satellite-v9",
attributionControl: false,
}}
viewport={viewport()}
onViewportChange={(evt) => setViewport(evt)}
ref={mapRef}
>
<Source
source={{
type: "raster-dem",
url: "mapbox://mapbox.terrain-rgb",
tileSize: 512,
maxzoom: 14,
}}
>
</Source>
</MapGL>
);
};
mapRef returns undefined
Hi! First off, fantastic work on the new docs and website. It's all looks fantastic! I really appreciate and low the attention and detail you've put into this project. Congrats.
I'm just working on adding it to a project and noticed that the map gets embedded with absolute positioning out of the box. I was wondering why this was the case and if it makes more sense to start with some generalized sane defaults instead?
Lastly an example that uses Maplibre would be wonderful :-)
It looks like the Interactive Map example isn't working as expected: https://stackblitz.com/edit/solid-map-gl-interactive?embed=1&file=src%2FApp.tsx. Actually none of the examples are working due to this error.
TypeError: mapLib.supported is not a function
solid-map-gl/src/components/MapGL/index.tsx
Line 133 in fc35853
Sience version 3 MapLibre GL does not exports supported()
maplibre/maplibre-gl-js#2591
I am using solid-map-gl v1.3.9 and dynamically changing a query parameter in my tiles URL via a store as shown here :
<Source
source={{
id: 'locations',
type: 'vector',
tiles: [
`http://example.com/locations/tiles/{z}/{x}/{y}.pbf?parameter=${
store.parameter.id
}`,
],
minzoom: 1,
maxzoom: 24,
bounds: [-180, -90, 180, 90],
}}
>
This worked (and continues to work if using v1.3.9) but on upgrading to v1.5.1 a change to store.parameter.id
does not trigger an update on the Source. Changes to store.parameter.id
does trigger update the Layer style definitions successfully so I'm thinking something has regressed in just the Source. I've looked through the changes a bit since 1.3.9 but nothing is jumping out as an obvious change that would cause this.
Hi again!
I've worked out how to add the Geocoder plugin to my map, by way of the useMap
function. I think you should add a comment to the README about how to do this, so that others know it's compatible with plugins.
Here's my code, for what it's worth:
import { useMap } from "solid-map-gl";
import { Component, onMount } from "solid-js";
import mapboxgl from "mapbox-gl";
import MapboxGeocoder from "@mapbox/mapbox-gl-geocoder";
import "@mapbox/mapbox-gl-geocoder/dist/mapbox-gl-geocoder.css";
const Geocoder: Component = () => {
const map = useMap();
onMount(async () => {
let accessToken = map()._requestManager._customAccessToken;
map().addControl(
new MapboxGeocoder({
accessToken: accessToken,
mapboxgl: mapboxgl,
})
);
});
return <></>;
};
export default Geocoder;
Cheers,
Cameron
Hi,
I just updated to the latest version and now I am getting the following dependency error:
[plugin:vite:import-analysis] Failed to resolve import "@babylonjs/core" from "node_modules/solid-map-gl/dist/source/components/Layer3D/index.jsx". Does the file exist?
...
14 | if (props.babylon) {
15 | //@ts-ignore
16 | BABYLON = await import('@babylonjs/core');
I am not using the Layer3D at all btw.
Thanks a lot for your libraryβit's really useful!
I'd like to allow the user to change the map type, switching between political (the default), aerial imagery, and a couple of other custom layers. I'm using SolidJS stores. This is what I have so far:
const Map: Component = () => {
const [store, { setMapLayer }] = useStore();
const style = () => store.map.styleURL;
return (
<MapGL
class={styles.Map}
options={{
accessToken: <my access token here>,
style: style(),
}}
viewport={{
center: [0, 15],
zoom: 2,
}}
></MapGL>
);
};
However when style() changes the map doesn't change. I think that's because it's lost reactivity, i.e. the value is read when the component is loaded but isn't read again.
Is there a way I could achieve this goal at the moment, or would the library need to be modified?
Cheers,
Cam
Seems like the following component produces an empty div when used:
import MapGL, { Source, Layer } from "solid-map-gl";
import 'mapbox-gl/dist/mapbox-gl.css';
export function MapTest(props) {
return (
<MapGL
options={{
style: 'mb:light',
accessToken: "MY_SECRET",
}}
viewport={{
center: [-122.41, 37.78],
zoom: 11,
}}
>
<Source
source={{
type: 'geojson',
data: 'https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson',
}}
>
<Layer
style={{
type: 'circle',
paint: {
'circle-radius': 5,
'circle-color': 'red',
},
}}
/>
</Source>
</MapGL>
);
};
export default MapTest;
Usage on a page:
<MapTest />
I am doing this in a 'export default function Page()' instead of a 'const App: Component = () => ...'. Does this matter?
No errors show up on console (Browser or server).
Any ideas? Thanks.
Hi there! Me again =) I'm trying to use solid-map-gl on a Solid 1.5 project with SolidStart (alpha-100). I dropped in the sample component and got a massive error:
import { Component, createSignal } from "solid-js";
import MapGL, { Viewport, Source, Layer } from "solid-map-gl";
const Map: Component = () => {
const [viewport, setViewport] = createSignal({
center: [-122.45, 37.78],
zoom: 11,
} as Viewport);
return (
<MapGL
options={{
accessToken: import.meta.env.VITE_MAPBOX_ACCESS_TOKEN,
style: "mb:light",
}}
viewport={viewport()}
>
<Source
source={{
type: "geojson",
data: "https://docs.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson",
}}
>
<Layer
style={{
type: "circle",
source: "earthquakes",
paint: {
"circle-radius": 8,
"circle-color": "red",
},
}}
/>
</Source>
</MapGL>
);
};
export default Map;
Resulting error due to CJS exports not existing.
SyntaxError: Named export 'MercatorCoordinate' not found. The requested module 'mapbox-gl' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'mapbox-gl';
I typically recommend using https://github.com/solidjs-community/rollup-preset-solid as a starting point for properly exporting libraries like this. With SolidStart becoming more stable the need for proper exports is important as well.
=)
I am dynamically constructing GeoJSON, retrieved at an interval from a remote API. If a user clicks on an item, I would like to be able to keep that item centered, even across interval loads. How might this be possible with solid-map-gl?
It's a bit unclear how to properly specify fitBounds. I have a range of markers and I'd like to ensure the viewport contains them all in view. I noticed that Control does allow for me to specify GeolocationOptions which has the fitBounds
property in it. However when I provide it with proper data the bounds don't take effect.
An example might be helpful for future users as this particular use-case is likely very common.
:-) Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.