Esempio n. 1
0
    def testIntersection(self):
        rect1 = QgsRectangle(0.0, 0.0, 5.0, 5.0)
        rect2 = QgsRectangle(2.0, 2.0, 7.0, 7.0)

        myMessage = "Expected: %s\nGot: %s\n" % (True, rect1.intersects(rect2))
        assert rect1.intersects(rect2), myMessage

        rect3 = rect1.intersect(rect2)
        self.assertFalse(rect3.isEmpty(), "Empty rectangle returned")

        myMessage = "Expected: %s\nGot: %s\n" % (3.0, rect3.width())
        assert rect3.width() == 3.0, myMessage

        myMessage = "Expected: %s\nGot: %s\n" % (3.0, rect3.height())
        assert rect3.height() == 3.0, myMessage
Esempio n. 2
0
    def testIntersection(self):
        rect1 = QgsRectangle(0.0, 0.0, 5.0, 5.0)
        rect2 = QgsRectangle(2.0, 2.0, 7.0, 7.0)

        myMessage = ('Expected: %s\nGot: %s\n' %
                     (True, rect1.intersects(rect2)))
        assert rect1.intersects(rect2), myMessage

        rect3 = rect1.intersect(rect2)
        self.assertFalse(rect3.isEmpty(), "Empty rectangle returned")

        myMessage = ('Expected: %s\nGot: %s\n' % (3.0, rect3.width()))
        assert rect3.width() == 3.0, myMessage

        myMessage = ('Expected: %s\nGot: %s\n' % (3.0, rect3.height()))
        assert rect3.height() == 3.0, myMessage
Esempio n. 3
0
    def testIntersection(self):
        rect1 = QgsRectangle(0.0, 0.0, 5.0, 5.0)
        rect2 = QgsRectangle(2.0, 2.0, 7.0, 7.0)

        self.assertTrue(rect1.intersects(rect2))

        rect3 = rect1.intersect(rect2)
        self.assertFalse(rect3.isEmpty(), "Empty rectangle returned")
        self.assertEqual(rect3.width(), 3.0)
        self.assertEqual(rect3.height(), 3.0)
Esempio n. 4
0
 def subdivideRecursively(self, rect, maxHeight):
   if maxHeight <= self.height:
     return
   self.subNodes = []
   for y in range(2):
     for x in range(2):
       xmin = self.rect.xMinimum() + 0.5 * x * self.rect.width()
       ymin = self.rect.yMinimum() + 0.5 * (1 - y) * self.rect.height()
       xmax = xmin + 0.5 * self.rect.width()
       ymax = ymin + 0.5 * self.rect.height()
       quadrect = QgsRectangle(xmin, ymin, xmax, ymax)
       node = QuadNode(self, quadrect, 2 * y + x, self.height + 1)
       self.subNodes.append(node)
       if quadrect.intersects(rect):
         node.subdivideRecursively(rect, maxHeight)
Esempio n. 5
0
 def subdivideRecursively(self, rect, maxHeight):
     if maxHeight <= self.height:
         return
     self.subNodes = []
     for y in range(2):
         for x in range(2):
             xmin = self.rect.xMinimum() + 0.5 * x * self.rect.width()
             ymin = self.rect.yMinimum() + 0.5 * (1 - y) * self.rect.height()
             xmax = xmin + 0.5 * self.rect.width()
             ymax = ymin + 0.5 * self.rect.height()
             quadrect = QgsRectangle(xmin, ymin, xmax, ymax)
             node = self.__class__(self, quadrect, 2 * y + x, self.height + 1)
             self.subNodes.append(node)
             if quadrect.intersects(rect):
                 node.subdivideRecursively(rect, maxHeight)
Esempio n. 6
0
class GSIElevTileProvider:
    def __init__(self, dest_wkt):
        self.dest_wkt = dest_wkt

        # crs transformer, which aims to calculate bbox in EPSG:3857
        self.crs3857 = QgsCoordinateReferenceSystem(3857)
        self.dest_crs = QgsCoordinateReferenceSystem()
        if not self.dest_crs.createFromWkt(dest_wkt):
            logMessage("Failed to create CRS from WKT: {0}".format(dest_wkt))
        self.transform = QgsCoordinateTransform(self.dest_crs, self.crs3857,
                                                QgsProject.instance())

        # approximate bbox of this data
        self.boundingbox = QgsRectangle(13667807, 2320477, 17230031, 5713298)

        self.downloader = Downloader()
        self.downloader.userAgent = "QGIS/{0} Qgis2threejs GSIElevTileProvider".format(
            Qgis.QGIS_VERSION_INT
        )  # will be overwritten in QgsNetworkAccessManager::createRequest() since 2.2
        self.downloader.DEFAULT_CACHE_EXPIRATION = QSettings().value(
            "/qgis/defaultTileExpiry", 24, type=int)

        self.driver = gdal.GetDriverByName("MEM")
        self.last_dataset = None

    def name(self):
        return "GSI Elevation Tile"

    def read(self, width, height, extent):
        # calculate bounding box in EPSG:3857
        geometry = extent.geometry()
        geometry.transform(self.transform)
        merc_rect = geometry.boundingBox()

        # if the bounding box doesn't intersect with the bounding box of this data, return a list filled with nodata value
        if not self.boundingbox.intersects(merc_rect):
            return [NODATA_VALUE] * width * height

        # get tiles
        over_smpl = 1
        segments_x = 1 if width == 1 else width - 1
        res = extent.width() / segments_x / over_smpl
        ds = self.getDataset(merc_rect.xMinimum(), merc_rect.yMinimum(),
                             merc_rect.xMaximum(), merc_rect.yMaximum(), res)

        geotransform = extent.geotransform(width, height)
        return self._read(ds, width, height, geotransform)

    def readValue(self, x, y):
        """Get value at the position using 1px * 1px memory raster. The value is calculated using a tile of max zoom level"""
        # coordinate transformation into EPSG:3857
        pt = self.transform.transform(QgsPoint(x, y))

        # if the point is not within the bounding box of this data, return nodata value
        if not self.boundingbox.contains(pt):
            return NODATA_VALUE

        res = 0.1
        hres = res / 2
        ds = self.getDataset(pt.x() - hres,
                             pt.y() - hres,
                             pt.x() + hres,
                             pt.y() + hres, res)

        geotransform = [x - hres, res, 0, y + hres, 0, -res]
        return self._read(ds, 1, 1, geotransform)[0]

    def _read(self, ds, width, height, geotransform):
        # create a memory dataset
        warped_ds = self.driver.Create("", width, height, 1, gdal.GDT_Float32)
        warped_ds.SetProjection(self.dest_wkt)
        warped_ds.SetGeoTransform(geotransform)

        # reproject image
        gdal.ReprojectImage(ds, warped_ds, None, None, gdal.GRA_Bilinear)

        # load values into an array
        band = warped_ds.GetRasterBand(1)
        fs = "f" * width * height
        return struct.unpack(
            fs, band.ReadRaster(0, 0, width, height,
                                buf_type=gdal.GDT_Float32))

    def getDataset(self, xmin, ymin, xmax, ymax, mapUnitsPerPixel):
        # calculate zoom level
        mpp1 = TSIZE1 / TILE_SIZE
        zoom = int(math.ceil(math.log(mpp1 / mapUnitsPerPixel, 2) + 1))
        zoom = max(0, min(zoom, ZMAX))

        # calculate tile range (yOrigin is top)
        size = TSIZE1 / 2**(zoom - 1)
        matrixSize = 2**zoom
        ulx = max(0, int((xmin + TSIZE1) / size))
        uly = max(0, int((TSIZE1 - ymax) / size))
        lrx = min(int((xmax + TSIZE1) / size), matrixSize - 1)
        lry = min(int((TSIZE1 - ymin) / size), matrixSize - 1)

        cols = lrx - ulx + 1
        rows = lry - uly + 1

        # download count limit
        if cols * rows > 128:
            logMessage("Number of tiles to fetch is too large!")
            width = height = 1
            return self.driver.Create("", width, height, 1, gdal.GDT_Float32,
                                      [])

        if self.last_dataset and self.last_dataset[0] == [
                zoom, ulx, uly, lrx, lry
        ]:  # if same as last tile set, return cached dataset
            return self.last_dataset[1]

        urltmpl = "http://cyberjapandata.gsi.go.jp/xyz/dem/{z}/{x}/{y}.txt"
        #urltmpl = "http://localhost/xyz/dem/{z}/{x}/{y}.txt"
        tiles = self.fetchFiles(urltmpl, zoom, ulx, uly, lrx, lry)

        # create a memory dataset
        width = cols * TILE_SIZE
        height = rows * TILE_SIZE
        res = size / TILE_SIZE
        geotransform = [
            ulx * size - TSIZE1, res, 0, TSIZE1 - uly * size, 0, -res
        ]

        #mem_driver = gdal.GetDriverByName("GTiff")
        #ds = mem_driver.Create("D:/fetched_tile.tif", width, height, 1, gdal.GDT_Float32, [])

        ds = self.driver.Create("", width, height, 1, gdal.GDT_Float32, [])
        ds.SetProjection(str(self.crs3857.toWkt()))
        ds.SetGeoTransform(geotransform)

        band = ds.GetRasterBand(1)
        for i, tile in enumerate(tiles):
            if tile:
                col = i % cols
                row = i // cols
                band.WriteRaster(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE,
                                 TILE_SIZE, tile)

        ds.FlushCache()

        self.last_dataset = [[zoom, ulx, uly, lrx, lry], ds]  # cache dataset
        return ds

    def fetchFiles(self, urltmpl, zoom, xmin, ymin, xmax, ymax):
        downloadTimeout = 60

        urls = []
        for y in range(ymin, ymax + 1):
            for x in range(xmin, xmax + 1):
                urls.append(
                    urltmpl.replace("{x}",
                                    str(x)).replace("{y}", str(y)).replace(
                                        "{z}", str(zoom)))
        files = self.downloader.fetchFiles(urls, downloadTimeout)

        for url in urls:
            data = files[url]
            if data:
                yield numpy.fromstring(data.replace(
                    b"e", NODATA_VALUE_BYTES).replace(b"\n", b","),
                                       dtype=numpy.float32,
                                       sep=",").tostring()  # to byte array
            else:
                array = numpy.empty(TILE_SIZE * TILE_SIZE, dtype=numpy.float32)
                array.fill(NODATA_VALUE)
                yield array.tostring()
Esempio n. 7
0
    def make_coco_dataset(self):
        layers = QgsProject.instance().mapLayers().values()
        vectorlayers = [layer for layer in layers if layer.type() == VectorLayer]
        rasterLayers = [layer for layer in layers if layer.type() == RasterLayer]

        vectorlayer = vectorlayers[self.dlg.vectorlayerselector.currentIndex()]
        rasterlayer = rasterLayers[self.dlg.rasterlayerselector.currentIndex()]

        img_size = int(self.dlg.imagesizeselector.currentText())
        scale_realworld = int(self.dlg.zoomlevel.currentText())
        buffer_size = int(self.dlg.buffersize.text()) / 100
        target_dir = self.dlg.dirselectline.text()
        dataset_description = str(self.dlg.datasetdescription.text())
        contributor = str(self.dlg.creatorname.text())
        url = str(self.dlg.url.text())
        version = str(self.dlg.datasetversion.text())
        license = str(self.dlg.licenseselector.currentText())

        # to implement
        scale_to_fit = self.dlg.scaletofit.isChecked()
        use_fieldcategory = bool(self.dlg.usecategoryfields.isChecked())
        categoryfield = self.dlg.categoryfields.currentText()


        QgsMessageLog.logMessage(str(scale_to_fit), "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage(str(use_fieldcategory), "cocotrainer", level=Qgis.Info)

        # prepare directories
        os.makedirs(os.path.join(target_dir, "train"), exist_ok=True)
        os.makedirs(os.path.join(target_dir, "val"), exist_ok=True)

        QgsMessageLog.logMessage("====================================", "cocotrainer", level=Qgis.Info)

        # TODO:
        #  - use field categories

        features_iterator = vectorlayer.getFeatures(QgsFeatureRequest().setFilterExpression('$area > 1'))

        features = [feature for feature in features_iterator]
        QgsMessageLog.logMessage("vector layer: " + vectorlayer.name(), "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage("Filtered polygons smaller than 1m2 from data", "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage("features: " + str(len(list(features))), "cocotrainer", level=Qgis.Info)

        options = QgsMapSettings()
        options.setLayers([rasterlayer])
        options.setOutputSize(QSize(int(img_size), int(img_size)))

        QgsMessageLog.logMessage(str(scale_realworld), "cocotrainer", level=Qgis.Info)
        QgsMessageLog.logMessage(str(img_size), "cocotrainer", level=Qgis.Info)

        date = datetime.now()

        cat_dict = {}
        if use_fieldcategory:
            categories = []
            # uniqueprovider = rasterlayer.dataProvider()
            fields = vectorlayer.fields()
            id = fields.indexFromName(categoryfield)
            uniquevalues = vectorlayer.uniqueValues(id)
            for i, val in enumerate(uniquevalues):
                categories.append({"supercategory": "object", "id": i, "name": str(val)})
                cat_dict[str(val)] = i
        else:
            categories = [{"supercategory": "object", "id": 0, "name": "CATEGORYNAME"}]
            cat_dict["CATEGORYNAME"] = 0

        coco_annotation = {
            "info": {
                "description": dataset_description,
                "url": url,
                "version": version,
                "year": int(date.strftime("%Y")),
                "contributor": contributor,
                "date_created": date.strftime("%d/%m/%y")
            },
            "licenses": [self.license_dict[license]],
            "images": [],
            "annotations": [],
            "categories": categories,
            # < -- Not in Captionsannotations
            "segment_info": []  # < -- Only in Panoptic annotations
        }

        bboxes, polygons = {}, {}
        for i, feature in enumerate(features):
            polygons[i] = feature.geometry()
            bboxes[i] = feature.geometry().boundingBox()

        if use_fieldcategory:
            cats = {}
            for i, feature in enumerate(features):
                cats[i] = str(feature[categoryfield])

        image_id = 0
        annotation_id = 0

        for i, feature in enumerate(features):
            self.dlg.progressBar.setValue((i / len(polygons) * 100))
            QgsMessageLog.logMessage("something is happening", "cocotrainer", level=Qgis.Info)
            geoms = feature.geometry()
            bbox = geoms.boundingBox()

            if scale_to_fit:
                xmax = bbox.xMaximum()
                ymax = bbox.yMaximum()
                ymin = bbox.yMinimum()
                xmin = bbox.xMinimum()

                diffx = xmax - xmin
                diffy = ymax - ymin
                xmax = xmax + (buffer_size * diffx)
                xmin = xmin - (buffer_size * diffx)
                ymax = ymax + (buffer_size * diffy)
                ymin = ymin - (buffer_size * diffy)

            else:
                midpoint = bbox.center()
                QgsMessageLog.logMessage("trying to log centerpoint", "cocotrainer", level=Qgis.Info)
                QgsMessageLog.logMessage(str(midpoint.x()), "cocotrainer", level=Qgis.Info)
                QgsMessageLog.logMessage(str(midpoint.y()), "cocotrainer", level=Qgis.Info)
                xmin = midpoint.x() - scale_realworld / 2
                xmax = midpoint.x() + scale_realworld / 2
                ymin = midpoint.y() - scale_realworld / 2
                ymax = midpoint.y() + scale_realworld / 2

            extent = QgsRectangle(xmin, ymin, xmax, ymax)

            fileid = "{}_{}_{}".format(xmin, ymin, scale_realworld)

            image = {
                "license": 0,
                "file_name": fileid + ".png",
                "coco_url": None,
                "height": img_size,
                "width": img_size,
                "date_captured": date.strftime("%d/%m/%y"),
                "flickr_url": None,
                "id": image_id
            }

            coco_annotation["images"].append(image)

            for j, geo in bboxes.items():
                if extent.contains(geo) or extent.intersects(geo):
                    points = polygons[j].asMultiPolygon()
                    xs = np.array([p.x() - xmin for p in points[0][0]])
                    ys = np.array([(ymax - ymin) - (p.y() - ymin) for p in points[0][0]])

                    xs *= (img_size / scale_realworld)
                    xs = np.round(xs)
                    xs = xs.astype(np.int32)

                    ys *= (img_size / scale_realworld)
                    ys = np.round(ys)
                    ys = ys.astype(np.int32)

                    ys[ys < 0] = 0
                    xs[xs < 0] = 0

                    polygon = [[int(p[0]), int(p[1])] for p in zip(xs, ys)]
                    flat_list = [item for sublist in polygon for item in sublist]
                    QgsMessageLog.logMessage(str(flat_list), "cocotrainer", level=Qgis.Info)

                    pixelposbbox = [int(np.min(xs)), (int(np.min(ys))), geo.width() * (img_size / scale_realworld),
                                    geo.height() * (img_size / scale_realworld)]
                    pixelposbbox = [number if number > 0 else 0 for number in pixelposbbox]

                    QgsMessageLog.logMessage(str([geo.xMinimum(), geo.yMaximum(), geo.width(), geo.height()]),
                                             "cocotrainer", level=Qgis.Info)
                    QgsMessageLog.logMessage(str(pixelposbbox), "cocotrainer", level=Qgis.Info)

                    annotation = {
                        "segmentation": [flat_list],  # format is [x1,y1,x2,y2]
                        "area": polygons[j].area(),
                        "iscrowd": 0,
                        "image_id": image_id,
                        "bbox": pixelposbbox,  # format is [top left x position, top left y position, width, height]
                        "category_id": cat_dict[cats[j]],
                        "id": annotation_id
                    }
                    annotation_id += 1
                    coco_annotation["annotations"].append(annotation)

            options.setExtent(extent)
            render = QgsMapRendererParallelJob(options)

            def finished():
                QgsMessageLog.logMessage("saving image to disk", "cocotrainer", level=Qgis.Info)
                fname = os.path.join(target_dir, 'train', "{}.png".format(fileid))
                img = render.renderedImage()
                if not img.save(fname, "png"):
                    print("Error saving image")

            render.finished.connect(finished)
            render.start()
            render.waitForFinished()
            image_id += 1

        QgsMessageLog.logMessage(str(coco_annotation), "cocotrainer", level=Qgis.Info)

        with open(os.path.join(target_dir, "annotation.json"), "w") as outfile:
            json.dump(coco_annotation, outfile)

        self.dlg.progressBar.setValue(100)
        self.dlg.finished_label.setText("parsed {} features".format(len(list(features))))
Esempio n. 8
0
class GSIElevTileProvider:

  def __init__(self, dest_wkt):
    self.dest_wkt = dest_wkt

    # crs transformer, which aims to calculate bbox in EPSG:3857
    self.crs3857 = QgsCoordinateReferenceSystem(3857)
    self.dest_crs = QgsCoordinateReferenceSystem()
    if not self.dest_crs.createFromWkt(dest_wkt):
      logMessage("Failed to create CRS from WKT: {0}".format(dest_wkt))
    self.transform = QgsCoordinateTransform(self.dest_crs, self.crs3857)

    # approximate bbox of this data
    self.boundingbox = QgsRectangle(13667807, 2320477, 17230031, 5713298)

    self.downloader = Downloader()
    self.downloader.userAgent = "QGIS/{0} Qgis2threejs GSIElevTileProvider".format(QGis.QGIS_VERSION)  # not written since QGIS 2.2
    self.downloader.DEFAULT_CACHE_EXPIRATION = QSettings().value("/qgis/defaultTileExpiry", 24, type=int)

    self.driver = gdal.GetDriverByName("MEM")
    self.last_dataset = None

  def name(self):
    return "GSI Elevation Tile"

  def read(self, width, height, extent):
    # calculate bounding box in EPSG:3857
    geometry = extent.geometry()
    geometry.transform(self.transform)
    merc_rect = geometry.boundingBox()

    # if the bounding box doesn't intersect with the bounding box of this data, return a list filled with nodata value
    if not self.boundingbox.intersects(merc_rect):
      return [NODATA_VALUE] * width * height

    # get tiles
    over_smpl = 1
    segments_x = 1 if width == 1 else width - 1
    res = extent.width() / segments_x / over_smpl
    ds = self.getDataset(merc_rect.xMinimum(), merc_rect.yMinimum(), merc_rect.xMaximum(), merc_rect.yMaximum(), res)

    geotransform = extent.geotransform(width, height)
    return self._read(ds, width, height, geotransform)

  def readValue(self, x, y):
    """Get value at the position using 1px * 1px memory raster. The value is calculated using a tile of max zoom level"""
    # coordinate transformation into EPSG:3857
    pt = self.transform.transform(QgsPoint(x, y))

    # if the point is not within the bounding box of this data, return nodata value
    if not self.boundingbox.contains(pt):
      return NODATA_VALUE

    res = 0.1
    hres = res / 2
    ds = self.getDataset(pt.x() - hres, pt.y() - hres, pt.x() + hres, pt.y() + hres, res)

    geotransform = [x - hres, res, 0, y + hres, 0, -res]
    return self._read(ds, 1, 1, geotransform)[0]

  def _read(self, ds, width, height, geotransform):
    # create a memory dataset
    warped_ds = self.driver.Create("", width, height, 1, gdal.GDT_Float32)
    warped_ds.SetProjection(self.dest_wkt)
    warped_ds.SetGeoTransform(geotransform)

    # reproject image
    gdal.ReprojectImage(ds, warped_ds, None, None, gdal.GRA_Bilinear)

    # load values into an array
    band = warped_ds.GetRasterBand(1)
    fs = "f" * width * height
    return struct.unpack(fs, band.ReadRaster(0, 0, width, height, buf_type=gdal.GDT_Float32))

  def getDataset(self, xmin, ymin, xmax, ymax, mapUnitsPerPixel):
    # calculate zoom level
    mpp1 = TSIZE1 / TILE_SIZE
    zoom = int(math.ceil(math.log(mpp1 / mapUnitsPerPixel, 2) + 1))
    zoom = max(0, min(zoom, ZMAX))

    # calculate tile range (yOrigin is top)
    size = TSIZE1 / 2 ** (zoom - 1)
    matrixSize = 2 ** zoom
    ulx = max(0, int((xmin + TSIZE1) / size))
    uly = max(0, int((TSIZE1 - ymax) / size))
    lrx = min(int((xmax + TSIZE1) / size), matrixSize - 1)
    lry = min(int((TSIZE1 - ymin) / size), matrixSize - 1)

    cols = lrx - ulx + 1
    rows = lry - uly + 1

    # download count limit
    if cols * rows > 128:
      logMessage("Number of tiles to fetch is too large!")
      width = height = 1
      return self.driver.Create("", width, height, 1, gdal.GDT_Float32, [])

    if self.last_dataset and self.last_dataset[0] == [zoom, ulx, uly, lrx, lry]:    # if same as last tile set, return cached dataset
      return self.last_dataset[1]

    urltmpl = "http://cyberjapandata.gsi.go.jp/xyz/dem/{z}/{x}/{y}.txt"
    #urltmpl = "http://localhost/xyz/dem/{z}/{x}/{y}.txt"
    tiles = self.fetchFiles(urltmpl, zoom, ulx, uly, lrx, lry)

    # create a memory dataset
    width = cols * TILE_SIZE
    height = rows * TILE_SIZE
    res = size / TILE_SIZE
    geotransform = [ulx * size - TSIZE1, res, 0, TSIZE1 - uly * size, 0, -res]

    #mem_driver = gdal.GetDriverByName("GTiff")
    #ds = mem_driver.Create("D:/fetched_tile.tif", width, height, 1, gdal.GDT_Float32, [])

    ds = self.driver.Create("", width, height, 1, gdal.GDT_Float32, [])
    ds.SetProjection(str(self.crs3857.toWkt()))
    ds.SetGeoTransform(geotransform)

    band = ds.GetRasterBand(1)
    for i, tile in enumerate(tiles):
      if tile:
        col = i % cols
        row = i / cols
        band.WriteRaster(col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE, tile)

    ds.FlushCache()

    self.last_dataset = [[zoom, ulx, uly, lrx, lry], ds]   # cache dataset
    return ds

  def fetchFiles(self, urltmpl, zoom, xmin, ymin, xmax, ymax):
    downloadTimeout = 60

    urls = []
    for y in range(ymin, ymax + 1):
      for x in range(xmin, xmax + 1):
        urls.append(urltmpl.replace("{x}", str(x)).replace("{y}", str(y)).replace("{z}", str(zoom)))
    files = self.downloader.fetchFiles(urls, downloadTimeout)

    for url in urls:
      data = files[url]
      if data:
        yield numpy.fromstring(data.replace("e", str(NODATA_VALUE)).replace("\n", ","), dtype=numpy.float32, sep=",").tostring()   # to byte array
      else:
        array = numpy.empty(TILE_SIZE * TILE_SIZE, dtype=numpy.float32)
        array.fill(NODATA_VALUE)
        yield array.tostring()