@@ -7,7 +7,14 @@ import {
77 getColorProperty ,
88 getListProperty ,
99 getNumberProperty ,
10- getStringProperty
10+ getStringProperty ,
11+ getTiledColor ,
12+ isBoxOverlay ,
13+ isImageBackground ,
14+ isPolylineOverlay ,
15+ setProperty ,
16+ validateTiledPoint ,
17+ validateTiledRectangle
1118} from './util.mjs' ;
1219
1320/**
@@ -233,6 +240,108 @@ export function convertTiledToDataMaps(map, mapName, language = 'en') {
233240 return datamap ;
234241}
235242
243+ /**
244+ * Converts a DataMaps map to the Tiled map format.
245+ * @param {DataMaps } datamaps DataMaps from all wikis to convert
246+ * @returns {TileMap } Converted Tiled map object
247+ */
248+ export function convertDataMapsToTiled ( datamaps ) {
249+ const datamap = datamaps . en ;
250+ if ( ! datamap ) {
251+ throw new Error ( 'English map data is required for conversion' ) ;
252+ }
253+ const metadata = new MetadataImpl ( datamap . custom ) ;
254+ const primaryBg = datamap . backgrounds . find ( isImageBackground ) ;
255+ if ( ! primaryBg ) {
256+ throw new Error ( 'At least one background with an image is required to convert to Tiled format' ) ;
257+ }
258+ const crsBR = validateTiledPoint ( datamap . crs . bottomRight ) ;
259+ const mapWidth = Math . ceil ( crsBR [ 0 ] / metadata . tileWidth ) ;
260+ const mapHeight = Math . ceil ( crsBR [ 1 ] / metadata . tileHeight ) ;
261+ const map = new TileMap ( ) ;
262+ map . tileWidth = metadata . tileWidth ;
263+ map . tileHeight = metadata . tileHeight ;
264+ map . width = mapWidth ;
265+ map . height = mapHeight ;
266+ if ( datamap . disclaimer ) {
267+ map . setProperty ( 'disclaimer' , datamap . disclaimer ) ;
268+ }
269+ if ( datamap . settings && datamap . settings . leaflet ) {
270+ map . setProperty ( 'popzoom' , datamap . settings . leaflet . uriPopupZoom ) ;
271+ }
272+ if ( datamap . include ) {
273+ map . setProperty ( 'include' , datamap . include . join ( '\n' ) ) ;
274+ }
275+ for ( const bg of datamap . backgrounds . filter ( isImageBackground ) ) {
276+ const layer = new ImageLayer ( bg . name || bg . image ) ;
277+ const fileName = metadata . getBackgroundFileName ( bg . image ) ;
278+ const filePath = FileInfo . joinPaths (
279+ tiled . project . folders [ 0 ] ,
280+ 'images' ,
281+ fileName
282+ ) ;
283+ layer . imageFileName = filePath ;
284+ layer . setProperty ( 'image' , bg . image ) ;
285+ map . addLayer ( layer ) ;
286+ }
287+ let annotationLayer = new ObjectGroup ( 'annotations' ) ;
288+ map . addLayer ( annotationLayer ) ;
289+ for ( const overlay of ( primaryBg . overlays || [ ] ) ) {
290+ const obj = new MapObject ( overlay . name ) ;
291+ if ( isBoxOverlay ( overlay ) ) {
292+ obj . shape = MapObject . Rectangle ;
293+ const [ [ x , y ] , [ x2 , y2 ] ] = validateTiledRectangle ( overlay . at ) ;
294+ obj . pos = { x, y } ;
295+ obj . width = x2 - x ;
296+ obj . height = y2 - y ;
297+ if ( overlay . color ) {
298+ obj . setProperty ( 'fill' , getTiledColor ( overlay . color ) ) ;
299+ }
300+ if ( overlay . borderColor ) {
301+ obj . setProperty ( 'border' , getTiledColor ( overlay . borderColor ) ) ;
302+ }
303+ } else if ( isPolylineOverlay ( overlay ) ) {
304+ obj . polygon = overlay . path
305+ . map ( p => validateTiledPoint ( p ) )
306+ . map ( p => ( { x : p [ 1 ] , y : p [ 0 ] } ) ) ;
307+ obj . pos = { x : 0 , y : 0 } ;
308+ obj . shape = MapObject . Polyline ;
309+ if ( overlay . color ) {
310+ obj . setProperty ( 'color' , getTiledColor ( overlay . color ) ) ;
311+ }
312+ if ( overlay . thickness ) {
313+ obj . setProperty ( 'thickness' , overlay . thickness ) ;
314+ }
315+ }
316+ annotationLayer . addObject ( obj ) ;
317+ }
318+ // TODO: Support nested layers
319+ for ( const [ layerName , markers ] of Object . entries ( datamap . markers ) . reverse ( ) ) {
320+ const layer = new ObjectGroup ( layerName ) ;
321+ for ( const m of markers ) {
322+ const obj = new MapObject ( m . name ) ;
323+ obj . pos = {
324+ x : m . x ,
325+ y : m . y
326+ } ;
327+ obj . shape = MapObject . Point ;
328+ obj . setProperties ( {
329+ page : m . article ,
330+ description : m . description ,
331+ image : m . image ,
332+ plain : m . isWikitext === undefined ? undefined : ! m . isWikitext
333+ } ) ;
334+ layer . addObject ( obj ) ;
335+ }
336+ map . addLayer ( layer ) ;
337+ }
338+ for ( const [ language , interwiki ] of Object . entries ( metadata . interwiki ) ) {
339+ setProperty ( map , 'name' , interwiki . mapName , language ) ;
340+ setProperty ( map , 'revision' , interwiki . revision , language ) ;
341+ }
342+ return map ;
343+ }
344+
236345/**
237346 * Converts a Tiled map to multiple DataMaps format for each language.
238347 * @param {TileMap } map Tiled map to convert to DataMaps
@@ -276,14 +385,30 @@ function generateWrite(multiple) {
276385 } ;
277386}
278387
388+ /**
389+ * Converts a DataMaps map to the Tiled map format.
390+ * @param {string } filePath Path to the file with the DataMaps map
391+ * @returns {TileMap } Tiled map
392+ */
393+ function read ( filePath ) {
394+ const file = new TextFile ( filePath , TextFile . ReadOnly ) ;
395+ const content = file . readAll ( ) ;
396+ file . close ( ) ;
397+ const datamap = JSON . parse ( content ) ;
398+ const isMultiple = typeof datamap . en === 'object' ;
399+ return convertDataMapsToTiled ( isMultiple ? datamap : { en : datamap } ) ;
400+ }
401+
279402tiled . registerMapFormat ( 'dataMaps' , {
280403 extension : 'mw-datamaps' ,
281404 name : 'DataMaps (all wikis)' ,
405+ read,
282406 write : generateWrite ( true )
283407} ) ;
284408
285409tiled . registerMapFormat ( 'dataMap' , {
286410 extension : 'mw-datamaps' ,
287411 name : 'DataMaps (single wiki)' ,
412+ read,
288413 write : generateWrite ( false )
289414} ) ;
0 commit comments