mmomtchev / rlayers

React Component Library for OpenLayers
ISC License
174 stars 35 forks source link

Is it possible to load tiles using the setTileLoadFunction ? #200

Open lucaselias99 opened 11 months ago

lucaselias99 commented 11 months ago

I'm using Ionic React in a project and would like to replicate a feature already used in another Ionic Angular application that uses OpenLayers. Basically, the code I want to replicate downloads the tiles from the application and saves them on the device. This way, when the app is accessed without an internet connection, it can display the tiles on the map, as shown below:

async addOrtofotoOffline() {
    this.networkStatus$
      .pipe(takeUntil(this.destroy$))
      .subscribe(async (status) => {
        if (!status) {
          const userLoggedValues = await firstValueFrom(
            this.userLoggedService.getUserLogged()
          );

          const tenantConfig = JSON.parse(userLoggedValues.tenantConfig);

          const exist = await this.validateFolderExists(tenantConfig.cidade);

          if (!exist) return;

          if (!this.validateFolderExists(tenantConfig.cidade)) {
            return;
          }

          const allLayersOrotofoto = this.map
            .getLayers()
            .getArray()
            .filter(
              (layer: any) => layer instanceof TileLayer && layer.get('online')
            );

          if (allLayersOrotofoto.length) {
            this.map.removeLayer(allLayersOrotofoto[0]);
          }

          const directory = FilesystemDirectory.Data;

          const source = new XYZ({
            url: `${directory}/${tenantConfig.cidade}/{z}/{x}/{y}.${userLoggedValues.ortofotoEXT}`,
            maxZoom: 20,
            crossOrigin: 'anonymous',
            tileLoadFunction: async (imageTile: any, src) => {
              imageTile.getImage().src = await this.readImageIfExists(src);
            },
          });

          const layerOptions = {
            source,
            zIndex: 5,
            visible: true,
            offline: true,
          };

          const layer = new TileLayer(layerOptions);

          this.map.addLayer(layer);
        }
      });
  }

I tried to replicate the same code for the rlayer based on this, but I was not successful. Could someone help me? Here's what I tried to do so far:

const diretorioTiles = "diretorio_tiles";

  const fetchTilesFromLote = async () => {
    if (!basemaps) return;

    const lote = await getAllLotes();
    const loteIds = lote.map((e) => e.fid);
    const endPoint = basemaps[0].url;
    const extensao = basemaps[0].extensao;

    const positions = await getTilesFromLote(loteIds);

    for await (const position of positions[0]) {
      const { data } = await axios.get(
        `${endPoint}/${position.z}/${position.x}/${position.y}.${extensao}`,
      );
      saveTile(position.z, position.x, position.y, extensao, data);
    }
  };

  async function saveTile(
    z: number,
    x: number,
    y: number,
    extensao: string,
    tileData: any,
  ) {
    const path = `${diretorioTiles}/${z}/${x}/${y}.${extensao}`;

    try {
      const res = await Filesystem.writeFile({
        path: path,
        data: tileData,
        directory: Directory.Documents,
        encoding: Encoding.UTF8,
        recursive: true,
      });

      console.log(`Tile salvo em ${path}`, res);
    } catch (error) {
      console.error("Erro ao salvar o tile:", error);
    }
  }

  const mapLayer = useRef<RLayerTile>(null);

  async function readImageIfExists(filename: string) {
    try {
      const file = await Filesystem.readFile({
        path: filename,
        directory: Directory.Documents,
      });

      return `data:image/jpeg;base64,${file.data}`;
    } catch (error) {
      return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAASwAAAEsAQMAAABDsxw2AAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAANQTFRFAAAAp3o92gAAAAF0Uk5TAEDm2GYAAAAiSURBVHic7cExAQAAAMKg9U9tCj+gAAAAAAAAAAAAAAB4GS20AAFmdDNkAAAAAElFTkSuQmCC";
    }
  }

  useEffect(() => {
    console.log("mapLayer.current", mapLayer.current);
    if (mapLayer.current) {
      mapLayer.current.source.setTileLoadFunction(async (tile, src) => {
        async (imageTile: any, src: any) => {
          imageTile.getImage().src = await readImageIfExists(src);
        };
      });
    }
  }, [mapLayer]);
mmomtchev commented 11 months ago

There is no reason why it should not work. Don't you have an idea what is wrong with your code?

mmomtchev commented 11 months ago

As you may have seen from my profile, I am currently living on social welfare because an extortion that involves the French police and the French judiciary, in order to intimidate me people are opening issues why simultaneously doing something else on my profile, yours is one, do you have anything to say about this?