|
|
@@ -0,0 +1,97 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "io"
|
|
|
+ "net/http"
|
|
|
+ "os"
|
|
|
+ "path/filepath"
|
|
|
+ "sync"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+func downloadTile(z, x, y int, wg *sync.WaitGroup, ch chan string, sem chan struct{}) {
|
|
|
+ defer wg.Done()
|
|
|
+ defer func() { <-sem }() // Release the semaphore slot
|
|
|
+
|
|
|
+ client := &http.Client{}
|
|
|
+
|
|
|
+ // Create a new request to the OpenStreetMap tile URL
|
|
|
+ url := fmt.Sprintf("https://tile.openstreetmap.org/%d/%d/%d.png", z, x, y)
|
|
|
+ req, err := http.NewRequest("GET", url, nil)
|
|
|
+ if err != nil {
|
|
|
+ ch <- fmt.Sprintf("Error creating request: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the User-Agent header
|
|
|
+ req.Header.Set("User-Agent", "MyTileDownloader/1.0")
|
|
|
+
|
|
|
+ // Make the request
|
|
|
+ response, err := client.Do(req)
|
|
|
+ if err != nil {
|
|
|
+ ch <- fmt.Sprintf("Error making request: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ defer response.Body.Close()
|
|
|
+
|
|
|
+ if response.StatusCode == http.StatusNotFound {
|
|
|
+ ch <- fmt.Sprintf("Tile %d/%d/%d not found (404)\n", z, x, y)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if response.StatusCode != http.StatusOK {
|
|
|
+ ch <- fmt.Sprintf("Failed to download tile %d/%d/%d: %s\n", z, x, y, response.Status)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create the destination directory
|
|
|
+ tilePath := filepath.Join(fmt.Sprintf("%d", z), fmt.Sprintf("%d", x), fmt.Sprintf("%d", y)+".png")
|
|
|
+ os.MkdirAll(filepath.Dir(tilePath), os.ModePerm)
|
|
|
+
|
|
|
+ outFile, err := os.Create(tilePath)
|
|
|
+ if err != nil {
|
|
|
+ ch <- fmt.Sprintf("Error creating file: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer outFile.Close()
|
|
|
+
|
|
|
+ _, err = io.Copy(outFile, response.Body)
|
|
|
+ if err != nil {
|
|
|
+ ch <- fmt.Sprintf("Error saving file: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ch <- fmt.Sprintf("Downloaded tile: z %d, x %d, y %d\n", z, x, y)
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ var wg sync.WaitGroup
|
|
|
+ ch := make(chan string)
|
|
|
+ sem := make(chan struct{}, 1500) // Semaphore with 1500 slots
|
|
|
+
|
|
|
+ // Goroutine to print messages from the channel
|
|
|
+ go func() {
|
|
|
+ for msg := range ch {
|
|
|
+ fmt.Print(msg)
|
|
|
+ }
|
|
|
+ }()
|
|
|
+ i := 0
|
|
|
+ j := 0
|
|
|
+ for z := 10; z < 19; z++ {
|
|
|
+ finz := 1 << z // equivalent to 2^z
|
|
|
+ for x := i; x < finz; x++ {
|
|
|
+ for y := j; y < finz; y++ {
|
|
|
+ wg.Add(1)
|
|
|
+ sem <- struct{}{} // Acquire a semaphore slot
|
|
|
+ go downloadTile(z, x, y, &wg, ch, sem)
|
|
|
+ time.Sleep(15 * time.Millisecond) // Pause between starting new goroutines
|
|
|
+ }
|
|
|
+ j = 0
|
|
|
+ i = 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ wg.Wait()
|
|
|
+ close(ch)
|
|
|
+}
|