Overview# This article explains how to accurately display annotation coordinates (xywh format) from IIIF (International Image Interoperability Framework) Presentation API v3 manifests on a map viewer using Leaflet-IIIF.
This problem may seem simple at first glance, but accurate coordinate conversion is not possible without understanding the internal workings of Leaflet-IIIF.
Background# In IIIF Presentation API v3, the target area of an annotation is specified in xywh format as follows:
{ } " " " " } " i t m b , t d y o o " " " a " p t d t v l r : e i y y a a g " v " p l n e " : a : e u g t h t " e u " t " i { : " a : t A o : g p n n " e " s n " T " " h : : e 雅 : t t x 屯 t / a " t 河 " p e t c u " j s x i o a , a : a o m l " / m n m B / p " e o e l , n d x e t y a . i " m o n , p r g l g " e / , . i o i r i g f / / i c i a i n f v / a c s a / n 1 v / a a s n / n 1 o # t x a y t w i h o = n 4 / 1 1 0 " 1 , 2 , 8 1 , 1 1 5 , 4 9 " This xywh=41012,81,115,49 means:
x: 41012 (left edge pixel position) y: 81 (top edge pixel position) w: 115 (width) h: 49 (height) These are pixel coordinates of the original image .
Leaflet-IIIF Coordinate System# Leaflet-IIIF is a Leaflet plugin that displays high-resolution images provided via the IIIF Image API in tile format. Internally it:
Uses the CRS.Simple coordinate reference system Manages images at multiple zoom levels with reduced sizes Uses different coordinate scales for each zoom level Due to this complex coordinate system, simply using map.unproject() or pointToLatLng() will not place annotations at the correct positions.
Trial and Error Process# Failed Attempt 1: Direct Use of map.unproject()# c c o o n n ❌ s s t t こ れ p l は o a 動 i t か n L な t n い g = = L . m p a o p i . n u t n ( p x r , o j y e ) c ; t ( p o i n t , 3 ) ; ズ ー ム レ ベ ル 3 を 指 定 Problem : unproject() assumes the current map coordinate system, which differs from the coordinate system used internally by Leaflet-IIIF.
Failed Attempt 2: Proportional Calculation from Map Bounds# c c c c c o o o o o n n n n n ❌ s s s s s t t t t t こ れ b n n l l も o o o n a 不 u r r g t 正 n m m 確 d X Y = = s = = b b = o o i i u u m m m n n a g g d d p X Y s s . . . g / g g e e e t i i t t B m m W N o a a e o u g g s r n e e t t d W H ( h s i e ) ( ( d i ) ) t g ; h h ; t ( ; n ( o n r o m r X m Y ( b ( o b u o n u d n s d . s g . e g t e E t a N s o t r ( t ) h ( - ) b - o u b n o d u s n . d g s e . t g W e e t s S t o ( u ) t ) h ) ( ; ) ) ) ; Problem : Since Leaflet-IIIF preserves the image aspect ratio, the map bounds and the actual image bounds differ.
Failed Attempt 3: Bounds Calculation Considering Aspect Ratio# c c i } } o o f n n e ❌ s s ( l t t i c c s 近 m o e い i m a n n が m a g 横 s s { 縦 完 a p e 長 t t 長 全 g A A の の に e s s 画 a v 画 は A p p 像 c e 境 像 境 一 s e e : t r 界 : 界 致 p c c 上 u t を 左 を し e t t 下 a i 調 右 調 な c に l c 整 に 整 い t = > 余 H a 余 白 e l 白 = m m i P a a g a i p p h d m W A t d a i s i g d p = n e t e g W h c m i t a = d / ) p t W ( h m { i m a d a / p t p H h H i e e m i / i a g g g h i h e t m t H ; a e g - i e g A a h s c t p t ; e u c a t l ; H e i g h t ) / 2 ; Problem : While this method can place annotations in approximately correct positions, it does not account for the reduced image sizes used internally by Leaflet-IIIF, resulting in slight misalignment.
The Correct Approach: Reproducing Leaflet-IIIF’s _fitBounds Method# Analyzing Leaflet-IIIF Source Code# Looking at Leaflet-IIIF’s _fitBounds method, we can see the following logic for placing images:
_ } f i t B v v v v v v v _ o a a a a a a a t u r r r r r r r h n i d _ i o i s n b s s t n f m w e o . : h i f a u _ i t s g = = n m f s i e e d a u a t S _ _ s p n = l i t t . c Z = z h h = f t t o e i i i i h o _ s s L t o i m t = . . . B n s h _ _ l o ( ; = i _ m m a u ) s t a a t n _ . h p p L d { t _ i . . n s h i s o o g ( i m . p p B b s a _ t t o o . g i i i u u _ e m o o n n g S a n n d d e i g s s s s t z e . . ( , I e S c c s n s i r r w t i . z s s , r t l e . . u i e s p p n e a n [ o o e ) l g i i i ) ; Z t n n n ; o h i t t o t T T m - i o o ( a L L _ 1 l a a t Z t t h - o L L i o n n s _ m g g . t ( ( _ h + L L m i . . a s o p p p . f o o . o f i i g p s n n e t e t t t i t ( ( S o ] 0 i i n ; , m z s a e . i g ( m m e ) a a S ) x g i ; N e z a S e t i . i z x v e , e . Z y 0 o ) ) o , , m ; i i n n i i t t i i a a l l Z Z o o o o m m ) ) ; ;
Key points :
_getInitialZoom() calculates the optimal zoom level_imageSizes[initialZoom + offset] gets the reduced image size at that zoom levelThat reduced size is used with pointToLatLng() for coordinate conversion Correct Implementation# f } u n ✅ c t c c c c c c c r こ i o o o o o o o e れ n n n n n n n t が n 1 s 2 s 3 s 4 s s 5 s s 6 u 正 . t . t . t . t t . t t . r 解 i n m L i オ o そ i 元 n n 縮 s s L a e n フ f の m 画 o o 小 c c e m g a i セ f ズ a 像 r r さ a a a a e f t ッ s ー g 座 m m れ l l f p T l i ト e ム e 標 X Y た e e l . o e a を t レ S を 画 d d e o L t l 計 ベ i 0 = = 像 X Y t p a - Z 算 = ル z - で 座 t t I o で e 1 i i の = = 標 i L I o i の の m m 座 に o n I m i 縮 = 範 g g 標 n n 変 n g F i 小 囲 X Y に o o 換 s ( の = f さ i に 変 r r ( . i 初 L れ i 正 / / 換 m m L c m 期 i a た i 規 X Y e r g ズ i y 画 f 化 i i a s X ー i e 像 L i i f . , ム f r サ a i i l p レ L . イ y f f i i e o i ベ a _ ズ e L L m m t i m ル y i を r a a a a - n g を e m 取 . y y g g I t Y 取 r a 得 _ e e e e I T ) 得 . g i r r S S I o _ e m . . i i F L { g S a x y z z と a e i g ; ; e e 同 t t z e . . じ L I e S x y 方 n n s i ; ; 法 g i . z ) ( t l e L i e s i i . a n [ i i p l g i i i o Z t n f f i o h i L L n o t a a t m - i y y ( ( a e e s m 1 l r r c a Z . . a p - o x y l . o e g i m = = d e i X t i + 元 元 , S f 画 画 i L o 像 像 s z a f の の c e y f 幅 高 a ( e s さ l ) r e e ) . t d ; o ] Y p ; ) t , i o i n n s i . t m i a a x l N Z a o t o i m v ) e ; Z o o m ; Complete Implementation Example# l l a } d e e s o t t y c n u m i c t } } m a i r e p i f y c n ; f u a t L n { c c c c c c m } i c s } t c . a c o o o o o o a ) i o e , c o a y t n n n n n n p ; i n t h n d e i マ s s s キ s s s マ I f ア s I T 2 s d r o ニ t t t ャ t t t ッ = c c z I L ノ t I i c c c c c f } a } 0 ( o E ; n フ ン プ e r o I a テ I m o o o o o u n ) 0 e l v ェ m r m バ c p i を L n s o F y ー a F e n n n n n n n ; 0 r e e i ス a e a ス a a m 作 . t : m 画 e シ n 画 o s s L s s s 画 c ア o ) r . n n ト n s n と n i a 成 m e : 像 r ョ n 像 u t t e t t t 像 t c c c c r ノ t c c c c c c L } ` ; o e t i を i p i 画 v n g a r L レ ン o が t a 座 i o o o o e テ a o o o o o o . ) ) r r L t 読 f o f 像 a t e p : . 0 イ = デ t 読 ( i i f i o i 標 o n n n n t ー t n n n n n n r . ; ) r i ( み e n e 情 s i S ( C ヤ ー a み ( m m l n f m か n s s s s u シ i s s s s s s e a o s ) 込 s s s 報 n e ' [ R ー L タ t 込 ) a a e i f a ら t t t t r ョ o t t t t t t c c w f f d < I { r t み t e t を = g r m 0 S を . i ま g g t t s g マ i n ン n t o e i i d s D ( e { U 取 A v a , . 追 t o れ = e e - i e e ッ m n n s s を P t x [ t b b a l i l l T t : 2 ' n r = = 得 m n i p S 加 i n る > W H I a t S プ a o o c c m 描 a a y x o o o n o g l l o r 秒 エ e l a n c ' 0 i l P ま i e I l i 座 g r r a a a 画 g r w , p t u g r h C O ( o $ 待 ラ r a a n o e , ] m e a で { d i I Z = z 標 e m m l l p e g h L t n l : t o p m n { つ ー ( = w w i I , p L g 待 t g F o e へ T X Y e e . . e y e o d e : l a a g i ( : ' a a f = d { l a e つ h h の o i の o d d o i t = , f m s ( ' o c p > n 画 ' D i i e e y t 内 m i = 変 L = = X Y p t t R b # 3 r i ) $ d 像 , O t t s c = , e = = 部 i 換 a t e = t w i = o f , : t . { e が M t a r = ロ = f i 関 t i i = = i m a , = g u f y b a x 読 e C / f r . n p . c i ジ L i 数 L m m o s a r h L n 0 ' : i n み r o d e e i v a i a i i ッ i a i n g g n n n . n g h i t . d 0 # n n + 込 r n a t s t a i i n i i ク i y f g X Y o o s f n e ] m l s 0 f 0 d o ま o t t c p e s n i v f i を i e L ( r r . o o t a = a , 0 f . P . 1 れ r e a h o m . t f a L f 再 f r a i / / m m c r . . = g t ' f 5 o b } る ) n / ( n s i i ( s a L 現 L . y m X Y r E t s e i L { , f p o ま ; t m m s [ t n i . y a a _ e g i i s a a p x T m n 0 u d で L a a e 0 e g m a e y y i r X m m . c r l y o a g 0 p y ) o n n . ] m A a n r e e m . , a a p h g i w L g B ' ( . a i i j ; s n g n . r r a _ g g i i o ( e t h a e o , ` v d f f s [ n e o x . . g i i e e m m i ( t ( . t T u a e e e o 0 o S t ; y _ e m m W H a a n a ; ' s L o n l d s s n ] . e a ; g S a g i e g g t n # p n L d u ' t t ( . b r t e i g Y d i e e T n x l g a s e , _ U ) i o v i t z e ) t g S S o o y i ( t ( } v r ; t d i o I e S h h i i L , w t x L t < i 3 l e y c n n s i { ; t z z a h ( , n o / n _ ) m . e s i . z ; e e t i = ' g p s i c ; s s I [ t l e . . L n ' , y ( L t t o [ e d 0 i e s x y n d ) ' ) x e r ) m 0 r ] a n [ ; ; g e [ ) ; f o ; p ] v + ; l g i ( x 1 . + t n a ; i Z t n L ) ] m , g c c o h i . ; a w > t e / o t p = p , b < . [ i m - i o > ( o b j 0 n ( a i N y t r s ] f m 1 l n { u t > o . o a Z t m + o n i . p - o ( b m ' d j . o s e h R ; ; s g i m c r ) i o e i a ) ; g n t i + l ; h ' S f e t ) i L o d ) . z a f X ; a e y f , d ( e s d ) r e s T ) . t c o ; o ] a ( p ; l m t e a i d p o Y ) n ) ; s , . m i a n x i N t a i t a i l v Z e o Z o o m o ) m ; ; Why This Method Is Correct# Image Sizes at Each Zoom Level# Leaflet-IIIF manages images at multiple zoom levels for performance:
_ ] i m 例 a : g L L L L L 4 e . . . . . 3 S p p p p p 8 i o o o o o 9 z i i i i i 0 e n n n n n s t t t t t x ( ( ( ( ( = 1 3 6 1 4 3 7 4 8 3 3 8 [ 2 3 6 7 8 8 , , , 2 9 7 , 0 5 1 3 6 , 5 0 0 1 の 2 4 8 2 3 画 ) ) ) 1 8 像 , , , 5 8 の ) 7 場 , 5 合 ) z z z z z o o o o o o o o o o m m m m m 0 1 2 3 8 ( m a x N a t i v e Z o o m ) The Role of initialZoom# _getInitialZoom() calculates the optimal zoom level for the map size:
_ } g e t I v v f } r n a a o e i r r r t t u i t o i i } r a o f v m f n l l f a a Z e s r g ( 2 o r e e i i r ; o a t i S m m e m n i a a t : c = = z g g u e e e e r f t t S S n u = h h = i i デ n i i z z i フ c 0 s s t e e ォ t . . . h . . - ル i 8 _ _ i x y ト o ; i i s o n m m . f a a _ f ( g g i t t s m e e m o o e a S S a l l t p i i g e e ; S z z e r r i e e S a a z s s i n n e . . z c c ) l l e e e e e s { n n [ < < g g i t t ] m m h h ; a a p p - - S S i i 1 1 z z ; e e - . . i x y t ) h > & i = & { s . 0 o ; p t i i - o - n ) s . { m a x N a t i v e Z o o m ; At this zoom level, the image is placed to fit neatly within the map.
The Meaning of Offset# offset adjusts the correspondence between the _imageSizes array index and the zoom level:
c o n s t o f f s e t = i i i f L a y e r . _ i m a g e S i z e s . l e n g t h - 1 - i i i f L a y e r . o p t i o n s . m a x N a t i v e Z o o m ;
For example, with maxNativeZoom = 8 and _imageSizes.length = 9, offset = 0.
Debugging Tips# If coordinate conversion is not working correctly, output the following information to the console for verification:
c c o o n n ❌ s s t t こ れ p l は o a 動 i t か n L な t n い g = = L . m p a o p i . n u t n ( p x r , o j y e ) c ; t ( p o i n t , 3 ) ; ズ ー ム レ ベ ル 3 を 指 定 0
Summary# To accurately display IIIF annotations in Leaflet-IIIF:
Understand Leaflet-IIIF’s internal logic : How the _fitBounds method places the imageUse the reduced image size : Use _imageSizes[initialZoom + offset] rather than the original image sizeConvert at the same zoom level : Convert with pointToLatLng(point, initialZoom)This method allows annotations to be placed with pixel-level accuracy.
References# Libraries used:Leaflet 1.9.4 Leaflet-IIIF 3.0.0 Created: October 19, 2025