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

feat: implementation for custom protocols #23

Closed
wants to merge 3 commits into from
Closed

Conversation

Kanahiro
Copy link
Member

@Kanahiro Kanahiro commented Nov 22, 2024

related close #8

What I did

Notes

  • Please tell me better interfaces if you have!

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a new "Protocols" link in the examples section for easier navigation.
    • Introduced a new mapping interface that supports custom tile protocols and displays vector and raster tiles.
    • Added documentation for custom protocols to enhance user understanding.
  • Enhancements

    • Integrated a new optional property for custom protocols in the mapping component, allowing for more flexibility in map configuration.
    • Updated project dependencies to include pmtiles for improved functionality.

These updates improve usability and expand functionality for users working with map interfaces.

@Kanahiro Kanahiro requested a review from ciscorn November 22, 2024 02:13
Copy link

cloudflare-workers-and-pages bot commented Nov 22, 2024

Deploying svelte-maplibre-gl with  Cloudflare Pages  Cloudflare Pages

Latest commit: 9f567e5
Status: ✅  Deploy successful!
Preview URL: https://6e625712.svelte-maplibre-gl.pages.dev
Branch Preview URL: https://feat-protocols.svelte-maplibre-gl.pages.dev

View logs

Copy link

coderabbitai bot commented Nov 22, 2024

Walkthrough

The pull request introduces several changes to the svelte-maplibre-gl project. A new dependency, pmtiles, is added to package.json. A new entry is included in the examples list in Index.svelte, linking to a new component Protocols.svelte, which implements a mapping interface using the pmtiles protocol. Additionally, a new markdown file content.svelte.md is created for documentation purposes. The MapLibre.svelte component is updated to support an optional protocols property for registering custom protocols with the map instance.

Changes

File Change Summary
package.json Added dependency "pmtiles": "^3.2.1" in devDependencies.
src/content/examples/Index.svelte Added new list item linking to /examples/protocols.
src/content/examples/protocols/Protocols.svelte Introduced new Svelte component for a map interface using custom tile protocols from pmtiles.
src/content/examples/protocols/content.svelte.md Created new markdown file for documentation of custom protocols with front matter and component render.
src/lib/maplibre/map/MapLibre.svelte Added optional property protocols to Props interface for registering protocols with maplibregl.

Possibly related PRs

  • Add CustomControl components #19: The changes in the main PR involve adding a new dependency (pmtiles) which is relevant to the new CustomControl components introduced in PR Add CustomControl components #19, as they enhance the functionality of the map interface, potentially utilizing the pmtiles package for custom controls.

Suggested reviewers

  • ciscorn

Poem

In the garden where maps unfold,
New protocols and tiles, a sight to behold.
With pmtiles now in our grasp,
Custom paths we joyfully clasp.
Hopping through code, with a leap and a bound,
Our mapping adventures know no ground! 🐰✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (6)
src/content/examples/Index.svelte (1)

18-18: Consider using a more descriptive link text

While the implementation is correct, consider making the link text more descriptive to better indicate the purpose of this example, such as "Custom Protocols" or "Protocol Handlers". This would maintain consistency with other descriptive example names in the list.

-	<li><a href="/examples/protocols">Protocols</a></li>
+	<li><a href="/examples/protocols">Custom Protocol Handlers</a></li>
src/content/examples/protocols/Protocols.svelte (4)

10-10: Prefix unused parameter with underscore

The second parameter is unused and should be prefixed with underscore to indicate intent.

-myprotocol: async (params, _) => {
+myprotocol: async (params, _unused) => {
🧰 Tools
🪛 eslint (1.23.1)

[error] 10-10: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)


14-32: Consider error handling for canvas operations

The canvas operations could fail in certain environments (e.g., no 2D context available). Consider adding error handling:

 const png = await new Promise((resolve) => {
   const canvas = document.createElement('canvas');
   canvas.width = 256;
   canvas.height = 256;
-  const context = canvas.getContext('2d')!;
+  const context = canvas.getContext('2d');
+  if (!context) {
+    throw new Error('Failed to get 2D context');
+  }

   // checkered pattern
   context.fillStyle = (z + x - y) % 2 === 0 ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)';
   context.fillRect(0, 0, canvas.width, canvas.height);

   // canvas to blob (png) to buffer
   canvas.toBlob(async (blob) => {
+    if (!blob) {
+      throw new Error('Failed to create blob');
+    }
     const buf = await blob!.arrayBuffer();
     resolve(buf);
   });
 });

43-44: Document the default map position

Consider adding a comment explaining why these specific coordinates (lng: 140.09085, lat: 37.3) and zoom level (6) were chosen as defaults.


50-52: Consider using theme-appropriate colors

The current hardcoded colors (red for transportation, blue for water) might be too simplistic. Consider:

  1. Using more subtle colors appropriate for a map theme
  2. Making colors configurable via props

Also applies to: 56-58

src/lib/maplibre/map/MapLibre.svelte (1)

31-32: Enhance property documentation

The comment could be more descriptive to better explain the purpose and usage of the protocols property.

-		// for addProtocol()
+		// Optional map of custom protocols to register with MapLibre GL JS.
+		// Each protocol is a function that implements custom data loading behavior.
+		// Example: { "myprotocol": (params) => { return { cancel: () => {} }; } }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c270f38 and 05bc2ba.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (5)
  • package.json (1 hunks)
  • src/content/examples/Index.svelte (1 hunks)
  • src/content/examples/protocols/Protocols.svelte (1 hunks)
  • src/content/examples/protocols/content.svelte.md (1 hunks)
  • src/lib/maplibre/map/MapLibre.svelte (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/content/examples/protocols/content.svelte.md
🧰 Additional context used
🪛 eslint (1.23.1)
src/content/examples/protocols/Protocols.svelte

[error] 4-4: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.

(@typescript-eslint/ban-ts-comment)


[error] 8-8: 'maplibregl' is not defined.

(no-undef)


[error] 10-10: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

🔇 Additional comments (4)
src/content/examples/protocols/Protocols.svelte (1)

1-64: Consider architectural improvements

Great implementation of custom protocols! Consider these architectural improvements:

  1. Move protocol definitions to a separate service file for better maintainability
  2. Add error boundaries around the map component to handle protocol failures gracefully
  3. Add unit tests for protocol handlers, especially the custom checkered pattern generator
  4. Consider adding TypeScript interfaces for protocol responses

Let's check if there are any existing protocol-related tests:

🧰 Tools
🪛 eslint (1.23.1)

[error] 4-4: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.

(@typescript-eslint/ban-ts-comment)


[error] 8-8: 'maplibregl' is not defined.

(no-undef)


[error] 10-10: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

package.json (1)

62-62: Verify pmtiles version compatibility

Let's check the latest stable version of pmtiles and verify its compatibility with maplibre-gl v5.0.0-pre.6

✅ Verification successful

pmtiles version is up-to-date and compatible

The specified pmtiles version (3.2.1) in package.json is the latest stable version available on npm. Since pmtiles doesn't declare any peer dependencies and maplibre-gl doesn't list it as a dependency, there are no version compatibility constraints to consider. The version can be safely used.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check latest version and compatibility of pmtiles

# Check latest version on npm
echo "Latest pmtiles version:"
npm view pmtiles version

# Check if pmtiles has maplibre-gl as a peer dependency
echo -e "\nPeer dependencies of pmtiles:"
npm view pmtiles peerDependencies

# Check if pmtiles is listed in maplibre-gl dependencies
echo -e "\nDependencies of maplibre-gl that might include pmtiles:"
npm view maplibre-gl dependencies

Length of output: 1171

src/lib/maplibre/map/MapLibre.svelte (2)

111-112: LGTM!

The protocols property is correctly destructured and follows the established pattern for optional properties.


156-159: Consider cleanup of registered protocols

The protocols remain registered even after the component is destroyed. Consider implementing cleanup logic in the onDestroy hook to prevent potential memory leaks or conflicts.

Let's verify if MapLibre GL JS provides a method to remove protocols:

If no built-in cleanup method exists, consider tracking registered protocols and warning users about potential conflicts:

 <script lang="ts">
+	// Track registered protocols for cleanup warnings
+	const registeredProtocols = new Set<string>();
+
 	// ... existing code ...
 
 	Object.entries(protocols ?? {}).forEach(([protocol, fn]) => {
+		registeredProtocols.add(protocol);
 		maplibregl.addProtocol(protocol, fn);
 	});
 
 	onDestroy(() => {
 		mapCtx.map = null;
 		map?.remove();
 		map = undefined;
+		// Warn about protocols that remain registered
+		if (registeredProtocols.size > 0) {
+			console.warn(
+				'The following protocols remain registered after component destruction:',
+				Array.from(registeredProtocols),
+				'This may cause conflicts if the protocols are re-registered.'
+			);
+		}
 	});
 </script>

import { Protocol } from 'pmtiles';
const pmtilesProtocol = new Protocol();

const protocols: Record<string, maplibregl.AddProtocolAction> = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add missing maplibregl type import

The maplibregl type needs to be imported to resolve the type error.

+import type maplibregl from 'maplibre-gl';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const protocols: Record<string, maplibregl.AddProtocolAction> = {
import type maplibregl from 'maplibre-gl';
const protocols: Record<string, maplibregl.AddProtocolAction> = {
🧰 Tools
🪛 eslint (1.23.1)

[error] 8-8: 'maplibregl' is not defined.

(no-undef)

<script lang="ts">
import { MapLibre, RasterTileSource, VectorTileSource, RasterLayer, LineLayer, FillLayer } from 'svelte-maplibre-gl';

// @ts-ignore
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace @ts-ignore with @ts-expect-error

Using @ts-expect-error is preferred as it will fail if the type error is fixed, preventing stale type suppressions.

-// @ts-ignore
+// @ts-expect-error pmtiles types not available
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// @ts-ignore
// @ts-expect-error pmtiles types not available
🧰 Tools
🪛 eslint (1.23.1)

[error] 4-4: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.

(@typescript-eslint/ban-ts-comment)

@@ -59,6 +59,7 @@
"maplibre-gl": "5.0.0-pre.6",
"mdsvex": "^0.12.3",
"mode-watcher": "^0.5.0",
"pmtiles": "^3.2.1",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider moving pmtiles to peerDependencies

Since pmtiles is used for implementing custom protocols functionality, it should be a peer dependency rather than a dev dependency. This ensures that applications using your library with custom protocols will have the required dependency available in production.

Apply this diff:

-    "pmtiles": "^3.2.1",

And add to the peerDependencies section:

   "peerDependencies": {
     "maplibre-gl": "^4.0.0 || ^5.0.0 || ^5.0.0-pre",
-    "svelte": "^5.0.0"
+    "svelte": "^5.0.0",
+    "pmtiles": "^3.2.1"
   },

Committable suggestion skipped: line range outside the PR's diff.

Comment on lines +156 to +159
// set protocols to maplibregl
Object.entries(protocols ?? {}).forEach(([protocol, fn]) => {
maplibregl.addProtocol(protocol, fn);
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for protocol registration

The current implementation doesn't handle potential errors during protocol registration. Consider wrapping the registration in a try-catch block to gracefully handle failures.

 	// set protocols to maplibregl
 	Object.entries(protocols ?? {}).forEach(([protocol, fn]) => {
+		try {
 			maplibregl.addProtocol(protocol, fn);
+		} catch (error) {
+			console.error(`Failed to register protocol '${protocol}':`, error);
+			// Optionally trigger the onerror callback if defined
+			onerror?.(new ErrorEvent('error', { 
+				error,
+				message: `Protocol registration failed: ${error.message}`
+			}));
+		}
 	});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// set protocols to maplibregl
Object.entries(protocols ?? {}).forEach(([protocol, fn]) => {
maplibregl.addProtocol(protocol, fn);
});
// set protocols to maplibregl
Object.entries(protocols ?? {}).forEach(([protocol, fn]) => {
try {
maplibregl.addProtocol(protocol, fn);
} catch (error) {
console.error(`Failed to register protocol '${protocol}':`, error);
// Optionally trigger the onerror callback if defined
onerror?.(new ErrorEvent('error', {
error,
message: `Protocol registration failed: ${error.message}`
}));
}
});

@Kanahiro
Copy link
Member Author

Kanahiro commented Nov 22, 2024

myprotocol doens't work, fix it...
done.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (3)
src/content/examples/protocols/Protocols.svelte (3)

10-10: Prefix unused parameter with underscore

The unused parameter should be prefixed with underscore to indicate it's intentionally unused.

-		myprotocol: async (params, _) => {
+		myprotocol: async (params, _unused) => {
🧰 Tools
🪛 eslint (1.23.1)

[error] 10-10: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)


20-22: Improve readability of checkered pattern logic

The pattern calculation could be more readable with a descriptive variable name.

-				context.fillStyle = (z + x - y) % 2 === 0 ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)';
+				const isLightSquare = (z + x - y) % 2 === 0;
+				context.fillStyle = isLightSquare ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.5)';

47-60: Consider extracting style values to constants

The hardcoded style values could be moved to constants for better maintainability.

+<script lang="ts">
+	const TRANSPORTATION_STYLE = {
+		'line-color': 'red'
+	};
+	const WATER_STYLE = {
+		'fill-color': 'blue'
+	};
+</script>

 	<VectorTileSource url="pmtiles://https://tile.openstreetmap.jp/static/planet.pmtiles">
 		<LineLayer
 			sourceLayer="transportation"
-			paint={{
-				'line-color': 'red'
-			}}
+			paint={TRANSPORTATION_STYLE}
 		/>
 		<FillLayer
 			sourceLayer="water"
-			paint={{
-				'fill-color': 'blue'
-			}}
+			paint={WATER_STYLE}
 		/>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 05bc2ba and 9f567e5.

📒 Files selected for processing (1)
  • src/content/examples/protocols/Protocols.svelte (1 hunks)
🧰 Additional context used
🪛 eslint (1.23.1)
src/content/examples/protocols/Protocols.svelte

[error] 4-4: Use "@ts-expect-error" instead of "@ts-ignore", as "@ts-ignore" will do nothing if the following line is error-free.

(@typescript-eslint/ban-ts-comment)


[error] 8-8: 'maplibregl' is not defined.

(no-undef)


[error] 10-10: '_' is defined but never used.

(@typescript-eslint/no-unused-vars)

🔇 Additional comments (2)
src/content/examples/protocols/Protocols.svelte (2)

6-6: LGTM: Protocol initialization is correct

The pmtiles Protocol is properly instantiated.


61-61: Verify the tile URL format

The tile URL template should be validated to ensure it matches the expected format.

✅ Verification successful

Based on the search results from the first script, I can generate the final response as we have sufficient evidence about tile URL patterns in the codebase.

The tile URL format is consistent with other examples in the codebase

The format myprotocol://{z}/{x}/{y} follows the same pattern as other tile URLs used throughout the codebase, which all use the {z}/{x}/{y} template structure. This includes various production examples like MapLibre demo tiles, elevation tiles, and other services. The custom protocol usage is valid as long as it's properly handled by the application.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if the tile URL template follows the standard format
# Expected format: protocol://{z}/{x}/{y}

rg -g '*.{ts,js,svelte}' '\{[zxy]\}' --no-filename --no-line-number

Length of output: 937


Script:

#!/bin/bash
# Let's check the RasterTileSource component implementation to understand the accepted tile URL formats
ast-grep --pattern 'class RasterTileSource {
  $$$
}'

# Also search for any documentation or type definitions related to tile URL formats
rg -g '*.{ts,js,svelte}' -i "tileurl|tile.*format" -B2 -A2

Length of output: 116

Comment on lines +18 to +19
const context = canvas.getContext('2d')!;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null checks for canvas context and blob

The code uses force unwrap operators (!) which could cause runtime errors if the canvas context or blob is null.

-				const context = canvas.getContext('2d')!;
+				const context = canvas.getContext('2d');
+				if (!context) {
+					throw new Error('Failed to get canvas context');
+				}

-					const buf = await blob!.arrayBuffer();
+					if (!blob) {
+						throw new Error('Failed to create blob from canvas');
+					}
+					const buf = await blob.arrayBuffer();

Also applies to: 25-27

Comment on lines +11 to +12
const zxy = params.url.replace('myprotocol://', '');
const [z, x, y] = zxy.split('/').map((v) => parseInt(v, 10));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add error handling for URL parsing

The URL parsing could fail if the format is incorrect. Consider adding validation and error handling.

-			const zxy = params.url.replace('myprotocol://', '');
-			const [z, x, y] = zxy.split('/').map((v) => parseInt(v, 10));
+			const zxy = params.url.replace('myprotocol://', '');
+			const parts = zxy.split('/');
+			if (parts.length !== 3) {
+				throw new Error('Invalid tile URL format');
+			}
+			const [z, x, y] = parts.map((v) => {
+				const num = parseInt(v, 10);
+				if (isNaN(num)) {
+					throw new Error('Invalid tile coordinates');
+				}
+				return num;
+			});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const zxy = params.url.replace('myprotocol://', '');
const [z, x, y] = zxy.split('/').map((v) => parseInt(v, 10));
const zxy = params.url.replace('myprotocol://', '');
const parts = zxy.split('/');
if (parts.length !== 3) {
throw new Error('Invalid tile URL format');
}
const [z, x, y] = parts.map((v) => {
const num = parseInt(v, 10);
if (isNaN(num)) {
throw new Error('Invalid tile coordinates');
}
return num;
});

@Kanahiro Kanahiro marked this pull request as draft November 22, 2024 03:28
@Kanahiro Kanahiro closed this Nov 22, 2024
@Kanahiro Kanahiro deleted the feat/protocols branch November 22, 2024 04:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

PMTiles Protocol
1 participant