ForHosting KIT · Travel & geography

S2 cube face from lat/lon

Find which of the six S2 cube faces a point falls on: San Francisco at 37.7749, -122.4194 lands on face 4, the -Y face, at u -0.918061185048 and v -0.635094359374. It is the first step before building S2 cells. Free in your browser.

  • Free
  • Runs in your browser
  • No sign-up
  • Your data stays on your device
Use it from WebAPIEmailTelegramApp soon
If you enter
Latitude
37.7749
Longitude
-122.4194
You get
Face
4
Face name
-Y
X
-0.4237560025
Y
-0.6672331383

Google's S2 geometry library tiles the Earth by projecting the unit sphere onto a cube, then recursively subdividing each face into a hierarchy of cells. Before you can encode an S2 cell id, compute a covering, or reason about region intersection, you need to know which of the six faces of that cube contains a given latitude and longitude. This capability accepts WGS84 lat and lon, converts them to a unit direction vector, and returns the S2 face index (0 through 5), a readable face name such as +X or -Z, the Cartesian unit components, and the UV coordinates of the point on that face in the conventional minus-one to plus-one range. Use it when you are building spatial indexes, teaching spherical geometry, or validating that your own S2 bindings pick the same face as the reference library for edge cases near face boundaries.

How lat/lon becomes an S2 cube face

Start with latitude and longitude in decimal degrees on the WGS84 sphere. Convert latitude to a polar angle from the equator and longitude to an azimuth from the prime meridian, both in radians. The unit direction vector is then x equals cos(latitude) cos(longitude), y equals cos(latitude) sin(longitude), and z equals sin(latitude), with positive z pointing toward the north pole and positive x toward latitude zero, longitude zero.

The six faces of the S2 cube correspond to the six half-spaces defined by the dominant Cartesian axis: face zero is the plus-X face, face one is plus-Y, face two is plus-Z, face three is minus-X, face four is minus-Y, and face five is minus-Z. Implementation follows the same largest-absolute-component rule used by Google's S2 library: compare the absolute values of x, y, and z; the axis with the largest magnitude decides the face pair; the sign of that component decides which of the two opposite faces is chosen.

On exact ties the higher axis index wins, matching S2's LargestAbsComponent ordering so that points on the seams between faces resolve deterministically. Once the face is known, UV coordinates on that face are obtained by dividing the two non-dominant components by the dominant one with the face-specific sign flips published in S2's ValidFaceXYZToUV, producing values in the closed interval from minus one to plus one that locate the point inside the square face before any further cell hierarchy is applied.

Where an S2 face lookup earns its keep

Education and exam generators that teach discrete global grids can ask students for the face of a city and grade against a deterministic oracle instead of floating teacher notebooks. Log analytics pipelines that bucket GPS samples by S2 face for coarse routing or A/B geography splits call this once per event and store the integer face next to the raw coordinates. Failed validation does not consume balance.

Keep requests small, pin the face integer and rounded UV in tests, and you get reproducible face assignment for indexes, lessons, and edge workers without standing up a full spherical geometry stack of your own.

S2 cell encoding preamble

Resolve which of the six cube faces holds a lat/lon before computing hierarchical S2 cell ids or region coverings in a worker that cannot load the full S2 library.

Discrete global grid teaching

Let students submit city coordinates and check the face index against an oracle that matches Google's largest-absolute-component face rule, including polar and seam edge cases.

Coarse geography bucketing

Bucket GPS events by S2 face for routing dashboards or A/B geo splits without computing full cell ids for every sample.

How do I find the S2 cube face for a latitude and longitude?

Send finite lat in [-90, 90] and lon in [-180, 180].

What do faces 0 through 5 mean?

They match Google's S2 convention: 0=+X, 1=+Y, 2=+Z (north), 3=-X, 4=-Y, 5=-Z (south). Choice is the largest absolute Cartesian component of the unit vector.

What are u and v in the response?

UV coordinates locate the point on the chosen cube face in the range [-1, 1], using the same ValidFaceXYZToUV sign conventions as S2.

What happens if latitude or longitude is out of range?

Latitude must be in [-90, 90] and longitude in [-180, 180]; invalid requests are not billed.

Which face does a point exactly on the seam between two faces get?

The tie is broken the same way every time: when two components of the unit vector are equally large, the axis with the higher index wins. That keeps points on the edges between faces from flipping between results.

Everything on this page is available programmatically. This section is for teams who want to wire it into their own systems; everyone else can just use the tool above.

POSThttps://api.kit.forhosting.com/geo/s2-face-from-latlon

Prefer to automate it? One authenticated POST creates the task; the result comes back by webhook or a signed link. The same capability also runs here on the web, by email and from Telegram — and soon from our app too.

curl -X POST https://api.kit.forhosting.com/geo/s2-face-from-latlon \
  -H "Authorization: Bearer $KIT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lat":37.7749,"lon":-122.4194}'
{
  "lat": 37.7749,
  "lon": -122.4194
}
{
  "task_id": "tsk_a1b2c3d4e5f6a1b2c3d4e5f6",
  "type": "geo.s2_face_from_latlon",
  "status": "queued",
  "_links": {
    "result": "/tasks/tsk_…/result"
  }
}

The API is asynchronous: the call returns a task_id immediately and the result arrives by webhook. Polling is capped at 1 req/s per task.

Per request$0.002

Published price — no tokens, no invented credits. A failed task is never charged.

HTTPCodeMeaning
401unauthorizedMissing or invalid API key.
402insufficient_balanceYour balance doesn't cover the task price.
404unknown_typeThat task type doesn't exist.
429rate_limitedToo many requests. Use the webhook instead of polling.

The algorithm is pure analytic math with no network calls and no random state, so the free browser path and the paid API path always agree.

Input contract, response fields, and validation errors

Example: San Francisco near latitude 37.7749 and longitude negative 122.4194 lands on face four (minus-Y) because the unit vector's largest absolute component is a negative y. The north pole (latitude 90, longitude 0) lands on face two (plus-Z). Equatorial longitude zero (latitude 0, longitude 0) lands on face zero (plus-X). POST a JSON body with two required finite numbers: lat (or alias latitude) between minus ninety and ninety inclusive, and lon (or aliases lng and longitude) between minus one hundred eighty and one hundred eighty inclusive. Numeric strings are accepted the same way as JSON numbers. Missing fields, empty strings, non-finite values such as NaN or Infinity, and out-of-range latitudes or longitudes fail with invalid_input and are not billed. On success the response echoes the cleaned lat and lon, the integer face from zero to five, face_name as one of +X, +Y, +Z, -X, -Y, or -Z, the unit vector components x, y, and z, the face-local UV pair u and v, and method set to s2_face_from_unit_sphere so clients can document the projection used. Pair this response with your own S2 cell encoder if you need full cell ids; this endpoint only identifies the face and the UV position on it.

Where an S2 face lookup earns its keep

Map and GIS platforms that expose S2 cell ids for region covering need a first-step face check that matches the library they use in production; a tiny parse API avoids shipping a full C++ or Java binding into every edge worker just to answer which cube face a pin sits on.

What does the S2 cube face from lat/lon tool cost?

The browser tool is free. API requests use prepaid KIT balance at $0.002 per successful call; invalid input is not billed.

Read the full KIT documentation →