@@ -21,6 +21,12 @@ interface CreateImageProps {
2121 image : ImageDatas | string ;
2222 options ?: Partial < StyleImageMetadata > ;
2323 debug ?: boolean ;
24+ /**
25+ * Force recreation when image dimensions change instead of throwing error.
26+ * When true (default), images are always removed and re-added to prevent dimension mismatch errors.
27+ * When false, uses the legacy behavior of trying updateImage first and falling back to recreation on error.
28+ */
29+ forceRecreateOnDimensionChange ?: boolean ;
2430}
2531
2632interface CreateImageActions {
@@ -38,6 +44,9 @@ interface CreateImageActions {
3844 * Composable for creating and managing MapLibre GL Images
3945 * Provides reactive image management with error handling, performance optimizations, and enhanced API
4046 *
47+ * **Important:** MapLibre GL requires that updated images have the same dimensions as the previous version.
48+ * This composable automatically handles dimension changes by removing and re-adding images when necessary.
49+ *
4150 * @param props - Configuration options for the image
4251 * @returns Enhanced actions and state for the image
4352 */
@@ -71,6 +80,41 @@ export function useCreateImage(props: CreateImageProps): CreateImageActions {
7180 return true ;
7281 }
7382
83+ /**
84+ * Gets the dimensions of an image data object
85+ * @param imageData - Image data to get dimensions from
86+ * @returns Object with width and height properties, or null if not determinable
87+ */
88+ function getImageDimensions (
89+ imageData : ImageDatas ,
90+ ) : { width : number ; height : number } | null {
91+ try {
92+ if ( imageData instanceof HTMLImageElement ) {
93+ return {
94+ width : imageData . naturalWidth || imageData . width ,
95+ height : imageData . naturalHeight || imageData . height ,
96+ } ;
97+ }
98+ if ( imageData instanceof ImageBitmap ) {
99+ return { width : imageData . width , height : imageData . height } ;
100+ }
101+ if ( imageData instanceof ImageData ) {
102+ return { width : imageData . width , height : imageData . height } ;
103+ }
104+ if (
105+ typeof imageData === 'object' &&
106+ 'width' in imageData &&
107+ 'height' in imageData
108+ ) {
109+ return { width : imageData . width , height : imageData . height } ;
110+ }
111+ return null ;
112+ } catch ( error ) {
113+ logError ( 'Error getting image dimensions:' , error ) ;
114+ return null ;
115+ }
116+ }
117+
74118 /**
75119 * Checks if the image exists on the map
76120 * @returns boolean indicating if image exists
@@ -138,8 +182,60 @@ export function useCreateImage(props: CreateImageProps): CreateImageActions {
138182
139183 // Update or add the image
140184 if ( hasImage ( ) ) {
141- map . updateImage ( props . id , imageData ) ;
142- imageStatus . value = ImageStatus . Updated ;
185+ // Check if we should force recreation when dimensions change
186+ const forceRecreate = props . forceRecreateOnDimensionChange ?? true ; // Default to true for better UX
187+
188+ if ( forceRecreate ) {
189+ // Safe approach: always remove and re-add to avoid dimension mismatch errors
190+ const newDimensions = getImageDimensions ( imageData ) ;
191+
192+ try {
193+ // Remove the existing image
194+ map . removeImage ( props . id ) ;
195+
196+ // Add the new image
197+ map . addImage ( props . id , imageData , props . options ) ;
198+ imageStatus . value = ImageStatus . Updated ;
199+ } catch ( recreateError ) {
200+ logError ( 'Error recreating image:' , recreateError , {
201+ imageId : props . id ,
202+ newDimensions,
203+ } ) ;
204+ throw recreateError ;
205+ }
206+ } else {
207+ // Legacy approach: try update first, fallback to recreate on dimension errors
208+ try {
209+ map . updateImage ( props . id , imageData ) ;
210+ imageStatus . value = ImageStatus . Updated ;
211+ } catch ( updateError : any ) {
212+ // If update fails due to dimension mismatch, remove and re-add the image
213+ if (
214+ updateError ?. message ?. includes ( 'width and height' ) ||
215+ updateError ?. message ?. includes ( 'same as the previous version' )
216+ ) {
217+ const newDimensions = getImageDimensions ( imageData ) ;
218+ logError (
219+ 'Image dimensions changed, removing and re-adding image:' ,
220+ updateError ,
221+ {
222+ imageId : props . id ,
223+ newDimensions,
224+ } ,
225+ ) ;
226+
227+ // Remove the existing image
228+ map . removeImage ( props . id ) ;
229+
230+ // Add the new image with updated dimensions
231+ map . addImage ( props . id , imageData , props . options ) ;
232+ imageStatus . value = ImageStatus . Created ;
233+ } else {
234+ // Re-throw if it's a different error
235+ throw updateError ;
236+ }
237+ }
238+ }
143239 } else {
144240 map . addImage ( props . id , imageData , props . options ) ;
145241 imageStatus . value = ImageStatus . Created ;
0 commit comments