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) }