The tile slicing rules of map services like Google, Gaode, and OpenStreetMap (OSM) differ from those of Baidu Maps primarily in two aspects:
Origin of the Tile Coordinate System:
For Google, Gaode, and OSM, the origin is set at 180° East longitude and 85.05° South latitude. This means the coordinate system’s bottom left corner corresponds to the southernmost and westernmost parts of the Earth. In contrast, Baidu Maps sets its origin at 0° East and 0° North, which is the intersection of the equator and the Greenwich Meridian. This change impacts how the tiles are divided and displayed on the map. Y-Axis Direction:
In Google, Gaode, and OSM maps, the Y-axis is oriented downwards. This means that the Y coordinate increases downwards from the origin, indicating movement from north to south, which aligns with the conventional Cartesian coordinate system. However, in Baidu Maps, the Y-axis is oriented upwards, meaning the Y coordinate increases upwards from the origin, indicating movement from south to north. This differing directional orientation requires developers to pay special attention to coordinate transformations and map rendering when working with different map services.
The code below use leaflet to load Baidu Map tiles:
<!DOCTYPE html>
<html>
<head>
<title>Baidu Map tiles</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<script src="https://cdn.bootcss.com/proj4js/2.4.3/proj4.js"></script>
<script src="https://cdn.bootcss.com/proj4leaflet/1.0.1/proj4leaflet.min.js"></script>
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
</body>
<script>
var baiduCrs = new L.Proj.CRS('EPSG:3857', '+proj=merc +a=6378206 +b=6356802.624 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs', {
resolutions: function() {
var level = 19
var res = [];
res[0] = Math.pow(2, 18);
for (var i = 1; i < level; i++) {
res[i] = Math.pow(2, (18 - i))
}
return res;
}(),
origin: [0, 0],
bounds: L.bounds([20037508.342789244, 0], [0, 20037508.342789244])
});
var map = new L.Map('map', { center: [41, 115], zoom: 3, crs: baiduCrs});
L.tileLayer('http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&scaler=1&p=1', {
subdomains: '0123456789',
tms: true,
attribution: 'Map data © <a href="https://map.baidu.com/">百度地图</a>'
}).addTo(map);
</script>
</html>
This example above works well.

How can I use flutter-map to load Baidu Map tiles? I wrote some code like this but the tiles on the map are messy:
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:proj4dart/proj4dart.dart' as proj4;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
final baiduProjection = proj4.Projection.add(
'EPSG:3857_Baidu',
'+proj=merc +a=6378206 +b=6356802.624 +lat_ts=0.0 +lon_0=0.0 +x_0=0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs',
);
final resolutions = _calculateBaiduScales();
final baiduCrs = Proj4Crs.fromFactory(
code: 'EPSG:3857',
proj4Projection: baiduProjection,
resolutions: resolutions,
origins: [Point(0, 0)],
bounds: Rect.fromLTRB(
0,
20037508.342789244,
20037508.342789244,
0,
),
);
return MaterialApp(
home: Scaffold(
body: FlutterMap(
options: MapOptions(
center: const LatLng(41, 115),
maxZoom: 19.0,
minZoom: 2.0,
zoom: 3.0,
crs: baiduCrs,
),
children: [
TileLayer(
urlTemplate: 'http://online{s}.map.bdimg.com/onlinelabel/?qt=tile&x={x}&y={y}&z={z}&styles=pl&scaler=1&p=1',
subdomains: const ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
// tms: true,
),
],
),
),
);
}
static List<double> _calculateBaiduScales() {
const level = 19;
final scales = <double>[];
scales.add(pow(2, 18).toDouble());
for (var i = 1; i < level; i++) {
scales.add(pow(2, (18 - i)).toDouble());
}
return scales;
}
}
Where did I go wrong?
